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,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;