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.
- package/Cargo.lock +2242 -0
- package/Cargo.toml +155 -0
- package/README.md +14 -6
- package/build.rs +5 -0
- package/castrum.darwin-arm64.node +0 -0
- package/castrum.darwin-x64.node +0 -0
- package/castrum.linux-arm64-gnu.node +0 -0
- package/castrum.linux-arm64-musl.node +0 -0
- package/castrum.linux-x64-gnu.node +0 -0
- package/castrum.linux-x64-musl.node +0 -0
- package/castrum.linux-x64-v3-gnu.node +0 -0
- package/castrum.win32-x64-msvc.node +0 -0
- package/dist/index.d.ts +1508 -1291
- package/dist/index.js +3391 -2069
- package/docs/API.md +2 -1
- package/docs/ARCHITECTURE.md +3 -2
- package/docs/CASE_STUDY.md +5 -3
- package/docs/ENVIRONMENT.md +25 -2
- package/docs/FFI_BUN_GUIDE.md +242 -14
- package/docs/HOW_TO_ADD_AN_OP.md +10 -0
- package/docs/REPO_MAP.md +17 -6
- package/docs/RND-CONCURRENCY.md +772 -0
- package/docs/bun-builtins-decision-matrix.md +4 -1
- package/index.ts +24 -25
- package/package.json +173 -156
- package/rust/crypto/aead.rs +378 -0
- package/rust/crypto/argon2.rs +265 -0
- package/rust/crypto/base64.rs +420 -0
- package/rust/crypto/bcrypt.rs +89 -0
- package/rust/crypto/cookie_sign.rs +169 -0
- package/rust/crypto/csrf.rs +106 -0
- package/rust/crypto/ed25519.rs +215 -0
- package/rust/crypto/hashing.rs +140 -0
- package/rust/crypto/hmac_sha256.rs +88 -0
- package/rust/crypto/jwt/api.rs +286 -0
- package/rust/crypto/jwt/mod.rs +22 -0
- package/rust/crypto/jwt/tests.rs +312 -0
- package/rust/crypto/jwt/token.rs +360 -0
- package/rust/crypto/mod.rs +27 -0
- package/rust/crypto/pbkdf2.rs +105 -0
- package/rust/crypto/random_token.rs +85 -0
- package/rust/crypto/session.rs +222 -0
- package/rust/ffi/crypto.rs +708 -0
- package/rust/ffi/hashing.rs +285 -0
- package/rust/ffi/http.rs +752 -0
- package/rust/ffi/ingress.rs +303 -0
- package/rust/ffi/json.rs +517 -0
- package/rust/ffi/jwt.rs +419 -0
- package/rust/ffi/metrics.rs +373 -0
- package/rust/ffi/mod.rs +69 -0
- package/rust/ffi/payload.rs +275 -0
- package/rust/ffi/probe.rs +60 -0
- package/rust/ffi/rate_limit.rs +77 -0
- package/rust/ffi/route.rs +85 -0
- package/rust/ffi/task.rs +172 -0
- package/rust/ffi/tests.rs +2333 -0
- package/rust/ffi/util.rs +152 -0
- package/rust/ffi/validators.rs +123 -0
- package/rust/http/accept.rs +606 -0
- package/rust/http/cookie_parser.rs +140 -0
- package/rust/http/etag.rs +289 -0
- package/rust/http/form.rs +136 -0
- package/rust/http/headers.rs +267 -0
- package/rust/http/http_date.rs +286 -0
- package/rust/http/http_parser.rs +252 -0
- package/rust/http/media_type.rs +355 -0
- package/rust/http/method.rs +200 -0
- package/rust/http/mime_lookup.rs +130 -0
- package/rust/http/mod.rs +32 -0
- package/rust/http/multipart.rs +685 -0
- package/rust/http/query_parser.rs +211 -0
- package/rust/http/url_codec.rs +296 -0
- package/rust/http/url_join.rs +324 -0
- package/rust/ingress/api.rs +355 -0
- package/rust/ingress/cors.rs +403 -0
- package/rust/ingress/ingress_constants.rs +209 -0
- package/rust/ingress/ip_trust.rs +312 -0
- package/rust/ingress/mod.rs +48 -0
- package/rust/ingress/native_route.rs +770 -0
- package/rust/ingress/options.rs +176 -0
- package/rust/ingress/output.rs +406 -0
- package/rust/ingress/packed.rs +191 -0
- package/rust/ingress/pipeline.rs +534 -0
- package/rust/ingress/proxy.rs +138 -0
- package/rust/ingress/rate_limit.rs +538 -0
- package/rust/ingress/terminal.rs +114 -0
- package/rust/ingress/tests.rs +567 -0
- package/rust/ingress/time.rs +70 -0
- package/rust/json/fast_schema/capture.rs +336 -0
- package/rust/json/fast_schema/compile.rs +556 -0
- package/rust/json/fast_schema/cursor.rs +368 -0
- package/rust/json/fast_schema/email.rs +282 -0
- package/rust/json/fast_schema/errors.rs +247 -0
- package/rust/json/fast_schema/mod.rs +57 -0
- package/rust/json/fast_schema/tests.rs +1143 -0
- package/rust/json/fast_schema/types.rs +140 -0
- package/rust/json/fast_schema/validate.rs +931 -0
- package/rust/json/json_ops.rs +121 -0
- package/rust/json/json_schema.rs +837 -0
- package/rust/json/json_ser.rs +883 -0
- package/rust/json/mod.rs +19 -0
- package/rust/json/napi_marshal.rs +318 -0
- package/rust/json/patch/api.rs +60 -0
- package/rust/json/patch/engine.rs +153 -0
- package/rust/json/patch/mod.rs +47 -0
- package/rust/json/patch/ops.rs +270 -0
- package/rust/json/patch/pointer.rs +49 -0
- package/rust/json/patch/tests.rs +499 -0
- package/rust/lib.rs +73 -0
- package/rust/metrics/api.rs +148 -0
- package/rust/metrics/mod.rs +21 -0
- package/rust/metrics/registry.rs +961 -0
- package/rust/panic_safety.rs +40 -0
- package/rust/payload/compress.rs +512 -0
- package/rust/payload/mod.rs +13 -0
- package/rust/payload/sse.rs +214 -0
- package/rust/payload/template.rs +253 -0
- package/rust/payload/websocket.rs +69 -0
- package/rust/payload/ws_frames.rs +354 -0
- package/rust/proptest_suite.rs +139 -0
- package/rust/selection.rs +95 -0
- package/rust/src/bin/native-bench.rs +495 -0
- package/rust/task/completion.rs +119 -0
- package/rust/task/mod.rs +65 -0
- package/rust/task/ops.rs +503 -0
- package/rust/task/runtime.rs +220 -0
- package/rust/task/tests.rs +231 -0
- package/rust/test_support.rs +71 -0
- package/rust/util/batch/api.rs +266 -0
- package/rust/util/batch/core.rs +473 -0
- package/rust/util/batch/mod.rs +23 -0
- package/rust/util/batch/tests.rs +445 -0
- package/rust/util/batch_core.rs +184 -0
- package/rust/util/bytes.rs +594 -0
- package/rust/util/mod.rs +34 -0
- package/rust/util/packed.rs +473 -0
- package/rust/util/text.rs +130 -0
- package/rust/util/threadpool.rs +128 -0
- package/rust/util/validation.rs +419 -0
- package/rust-toolchain.toml +17 -0
- package/scripts/postinstall.mjs +199 -0
- package/src/ingress/constants.ts +1 -1
- package/src/ingress/context.ts +2 -2
- package/src/ingress/decode/baked-result.ts +13 -13
- package/src/ingress/decode/fast-result.ts +17 -17
- package/src/ingress/errors.ts +7 -7
- package/src/ingress/headers/baked-templates.ts +1 -1
- package/src/ingress/headers/fast-templates.ts +3 -3
- package/src/ingress/health.ts +1 -2
- package/src/ingress/index.ts +1 -1
- package/src/ingress/native-route.ts +9 -5
- package/src/ingress/options.ts +1 -1
- package/src/ingress/packing/gather-raw-headers.ts +1 -1
- package/src/ingress/packing/route-wire.ts +1 -5
- package/src/ingress/response/error-bodies.ts +5 -5
- package/src/ingress/response/terminal.ts +1 -1
- package/src/ingress/routes/echo.ts +4 -4
- package/src/ingress/routes/fallback.ts +2 -2
- package/src/ingress/routes/head.ts +2 -2
- package/src/ingress/routes/native.ts +2 -1
- package/src/ingress/routes/options.ts +1 -1
- package/src/ingress/routes/read.ts +2 -2
- package/src/ingress/server.ts +10 -6
- package/src/ingress/status.ts +7 -7
- package/src/integration/batch.ts +1 -1
- package/src/integration/index.ts +9 -9
- package/src/integration/pipeline.ts +6 -6
- package/src/integration/websocket.ts +2 -2
- package/src/loader/index.ts +1 -5
- package/src/loader/ops.ts +1 -1
- package/src/native/ffi/build/codecs.ts +130 -12
- package/src/native/ffi/build/compress.ts +22 -10
- package/src/native/ffi/build/instances.ts +46 -4
- package/src/native/ffi/build/metrics.ts +247 -0
- package/src/native/ffi/build/parse.ts +59 -23
- package/src/native/ffi/build/task.ts +79 -0
- package/src/native/ffi/build/util.ts +32 -1
- package/src/native/ffi/build.ts +4 -0
- package/src/native/ffi/selftest.ts +12 -3
- package/src/native/ffi/types.ts +151 -9
- package/src/native/ffi.ts +160 -15
- package/src/native/index.ts +1 -1
- package/src/native/loader.ts +55 -5
- package/src/native/types/instances.ts +34 -0
- package/src/native/types.ts +11 -0
- package/src/runtime/codec.ts +53 -9
- package/src/runtime/types.ts +7 -0
- package/src/rust-ffi/batch/build.ts +16 -9
- package/src/rust-ffi/index.ts +7 -6
- package/src/rust-ffi/options.ts +1 -3
- package/src/rust-ffi/proven.ts +3 -3
- package/src/rust-ffi/scalar/crypto.ts +1 -1
- package/src/rust-ffi/scalar/factories.ts +73 -0
- package/src/rust-ffi/scalar/hashing.ts +1 -1
- package/src/rust-ffi/scalar/http.ts +71 -27
- package/src/rust-ffi/scalar/index.ts +2 -0
- package/src/rust-ffi/scalar/interface.ts +20 -0
- package/src/rust-ffi/scalar/payload.ts +11 -3
- package/src/rust-ffi/scalar/validation.ts +121 -0
- package/src/rust-ffi/text.ts +25 -3
- package/src/selection.ts +1 -1
- package/src/shared/bytes.ts +24 -4
- package/src/shared/codec.ts +18 -3
- package/src/task/index.ts +24 -0
- package/src/task/op.ts +171 -0
- package/src/task/runtime.ts +568 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//! NAPI projection of the metrics registry (`MetricsRegistry` JS class).
|
|
2
|
+
//!
|
|
3
|
+
//! Ergonomic surface for Node / the napi fallback transport: labels cross as
|
|
4
|
+
//! string arrays here (the C-ABI hot path packs them `\x1f`-separated instead
|
|
5
|
+
//! — see `rust/ffi/metrics.rs`). Both wrap the SAME pure core, so outputs are
|
|
6
|
+
//! byte-identical across transports.
|
|
7
|
+
|
|
8
|
+
use napi::bindgen_prelude::*;
|
|
9
|
+
use napi_derive::napi;
|
|
10
|
+
|
|
11
|
+
use super::registry::{MetricsKind, MetricsRegistry as CoreRegistry};
|
|
12
|
+
|
|
13
|
+
/// Pack optional label values into the core's `\x1f`-separated wire format.
|
|
14
|
+
fn pack(values: Option<&Vec<String>>) -> Vec<u8> {
|
|
15
|
+
let Some(vals) = values else {
|
|
16
|
+
return Vec::new();
|
|
17
|
+
};
|
|
18
|
+
let need = vals.iter().map(String::len).sum::<usize>() + vals.len().saturating_sub(1);
|
|
19
|
+
let mut out = Vec::with_capacity(need);
|
|
20
|
+
for (i, v) in vals.iter().enumerate() {
|
|
21
|
+
if i > 0 {
|
|
22
|
+
out.push(super::registry::METRICS_VALUE_SEP);
|
|
23
|
+
}
|
|
24
|
+
out.extend_from_slice(v.as_bytes());
|
|
25
|
+
}
|
|
26
|
+
out
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fn throw<E: std::fmt::Display>(e: E) -> Error {
|
|
30
|
+
Error::new(Status::InvalidArg, e.to_string())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Borrow the optional label list as `&[&str]` (lives for the call).
|
|
34
|
+
fn borrowed_keys(labels: &Option<Vec<String>>) -> Vec<&str> {
|
|
35
|
+
labels
|
|
36
|
+
.as_ref()
|
|
37
|
+
.map_or(Vec::new(), |l| l.iter().map(String::as_str).collect())
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Sharded counters / gauges / histograms registry with a Prometheus
|
|
41
|
+
/// text-format render. Series are declared once (returning an id) and updated
|
|
42
|
+
/// per event; render is deterministic across calls.
|
|
43
|
+
///
|
|
44
|
+
/// @example
|
|
45
|
+
/// ```ts
|
|
46
|
+
/// const m = new MetricsRegistry()
|
|
47
|
+
/// const hits = m.counter('http_requests_total', ['route', 'status'])
|
|
48
|
+
/// m.record(hits, ['/a', '200'], 1)
|
|
49
|
+
/// m.render() // "# TYPE http_requests_total counter\nhttp_requests_total{...} 1\n"
|
|
50
|
+
/// ```
|
|
51
|
+
#[napi]
|
|
52
|
+
pub struct MetricsRegistry {
|
|
53
|
+
inner: CoreRegistry,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[napi]
|
|
57
|
+
impl MetricsRegistry {
|
|
58
|
+
/// Create an empty registry.
|
|
59
|
+
#[napi(constructor)]
|
|
60
|
+
#[allow(clippy::new_without_default)] // napi constructors can't derive Default
|
|
61
|
+
pub fn new() -> Self {
|
|
62
|
+
Self {
|
|
63
|
+
inner: CoreRegistry::new(),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Declare a counter family → its series id (idempotent per shape).
|
|
68
|
+
#[napi]
|
|
69
|
+
pub fn counter(&self, name: String, labels: Option<Vec<String>>) -> Result<u32> {
|
|
70
|
+
let keys: Vec<&str> = borrowed_keys(&labels);
|
|
71
|
+
self.inner
|
|
72
|
+
.declare(MetricsKind::Counter, &name, &keys, &[])
|
|
73
|
+
.map_err(throw)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Declare a gauge family → its series id (idempotent per shape).
|
|
77
|
+
#[napi]
|
|
78
|
+
pub fn gauge(&self, name: String, labels: Option<Vec<String>>) -> Result<u32> {
|
|
79
|
+
let keys: Vec<&str> = borrowed_keys(&labels);
|
|
80
|
+
self.inner
|
|
81
|
+
.declare(MetricsKind::Gauge, &name, &keys, &[])
|
|
82
|
+
.map_err(throw)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Declare a histogram family → its series id. Empty/omitted `buckets`
|
|
86
|
+
/// selects the default latency buckets (sorted + deduped internally).
|
|
87
|
+
#[napi]
|
|
88
|
+
pub fn histogram(
|
|
89
|
+
&self,
|
|
90
|
+
name: String,
|
|
91
|
+
labels: Option<Vec<String>>,
|
|
92
|
+
buckets: Option<Vec<f64>>,
|
|
93
|
+
) -> Result<u32> {
|
|
94
|
+
let keys: Vec<&str> = borrowed_keys(&labels);
|
|
95
|
+
self.inner
|
|
96
|
+
.declare(
|
|
97
|
+
MetricsKind::Histogram,
|
|
98
|
+
&name,
|
|
99
|
+
&keys,
|
|
100
|
+
&buckets.unwrap_or_default(),
|
|
101
|
+
)
|
|
102
|
+
.map_err(throw)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Counter/gauge `+= amount`, or histogram observe `amount`.
|
|
106
|
+
#[napi]
|
|
107
|
+
pub fn record(&self, series: u32, values: Option<Vec<String>>, amount: f64) -> Result<()> {
|
|
108
|
+
self.inner
|
|
109
|
+
.add(series, &pack(values.as_ref()), amount)
|
|
110
|
+
.map_err(throw)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Gauge assignment (`= value`).
|
|
114
|
+
#[napi]
|
|
115
|
+
pub fn gauge_set(&self, series: u32, values: Option<Vec<String>>, value: f64) -> Result<()> {
|
|
116
|
+
self.inner
|
|
117
|
+
.set(series, &pack(values.as_ref()), value)
|
|
118
|
+
.map_err(throw)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Render the Prometheus text exposition format (deterministic order).
|
|
122
|
+
/// Invalid UTF-8 in label values becomes replacement chars here.
|
|
123
|
+
#[napi]
|
|
124
|
+
pub fn render(&self) -> String {
|
|
125
|
+
String::from_utf8_lossy(&self.inner.render()).into_owned()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Opaque handle for the C-ABI fast path (`castrum_metrics_*` symbols).
|
|
129
|
+
/// Only valid while THIS instance is alive; the JS wrapper holds it.
|
|
130
|
+
#[napi]
|
|
131
|
+
pub fn inner_ptr(&self) -> u64 {
|
|
132
|
+
&self.inner as *const CoreRegistry as u64
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Number of live series (tests / introspection).
|
|
136
|
+
#[napi]
|
|
137
|
+
pub fn series_count(&self) -> u32 {
|
|
138
|
+
self.inner.series_count() as u32
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Dump every live series in the packed v1 snapshot format (families then
|
|
142
|
+
/// series — see the registry core for the exact layout). The machine-
|
|
143
|
+
/// readable read path for exporters (OTLP) and tests.
|
|
144
|
+
#[napi]
|
|
145
|
+
pub fn snapshot(&self) -> Buffer {
|
|
146
|
+
self.inner.snapshot().into()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//! Metrics — a sharded, zero-dependency counters / gauges / histograms
|
|
2
|
+
//! registry with a Prometheus text-format renderer.
|
|
3
|
+
//!
|
|
4
|
+
//! Pure-Rust core (no napi types in signatures) so it stays unit-testable with
|
|
5
|
+
//! plain `cargo test`; the `#[napi]` projection lives in `api.rs` and the C-ABI
|
|
6
|
+
//! (`castrum_metrics_*`) exports in `rust/ffi/metrics.rs`. Both entry points
|
|
7
|
+
//! wrap the SAME [`registry::MetricsRegistry`] core.
|
|
8
|
+
//!
|
|
9
|
+
//! Design notes:
|
|
10
|
+
//! - Series are addressed by a `u32` family id returned at declaration time;
|
|
11
|
+
//! per-label-set state lives in 64 lock-sharded hash maps keyed by the
|
|
12
|
+
//! packed label bytes, so the hot path is one mutex acquire + one hash.
|
|
13
|
+
//! - Label VALUES cross the C ABI packed in ONE buffer separated by `\x1f`
|
|
14
|
+
//! (unit separator); values must not contain `\x1f` or NUL (validated).
|
|
15
|
+
//! - Render output is deterministic: families in declaration order, series
|
|
16
|
+
//! sorted by raw label bytes within each family.
|
|
17
|
+
|
|
18
|
+
pub mod api;
|
|
19
|
+
pub mod registry;
|
|
20
|
+
|
|
21
|
+
pub use self::registry::MetricsRegistry;
|