castrum 0.9.1 → 0.9.2
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/README.md +1 -1
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +239 -5
- package/dist/index.js +3852 -3375
- package/docs/ARCHITECTURE.md +11 -9
- package/docs/BENCHMARKS.md +70 -38
- package/docs/CASE_STUDY.md +10 -7
- package/docs/ENVIRONMENT.md +16 -1
- package/docs/FFI_BUN_GUIDE.md +18 -10
- package/docs/GETTING_STARTED.md +5 -4
- package/docs/HOW_TO_ADD_AN_OP.md +180 -0
- package/docs/INGRESS-ROUTER.md +3 -2
- package/docs/INGRESS.md +6 -3
- package/docs/NATIVE-ROUTE.md +28 -4
- package/docs/REPO_MAP.md +60 -30
- package/docs/adr/0001-two-wire-formats.md +1 -1
- package/docs/ai/LOCAL_DEV.md +81 -0
- package/docs/ai/TREE.md +1070 -0
- package/package.json +24 -23
- package/src/ingress/decode/result-base.ts +10 -2
- package/src/ingress/handlers.ts +45 -290
- package/src/ingress/headers/security.ts +41 -0
- package/src/ingress/index.ts +12 -148
- package/src/ingress/native-route.ts +182 -0
- package/src/ingress/packing/gather-raw-headers.ts +26 -2
- package/src/ingress/packing/route-wire.ts +197 -0
- package/src/ingress/packing/select-headers.ts +8 -1
- package/src/ingress/path-matcher.ts +115 -0
- package/src/ingress/response/baked-response.ts +302 -0
- package/src/ingress/router.ts +42 -2
- package/src/ingress/routes/common.ts +9 -1
- package/src/ingress/routes/json-write.ts +21 -18
- package/src/ingress/routes/native.ts +130 -0
- package/src/ingress/server-node.ts +1 -1
- package/src/ingress/server.ts +38 -99
- package/src/ingress/sync.ts +151 -0
- package/src/ingress/types.ts +9 -0
- package/src/native/ffi/build/codecs.ts +736 -0
- package/src/native/ffi/build/compress.ts +248 -0
- package/src/native/ffi/build/instances.ts +598 -0
- package/src/native/ffi/build/parse.ts +539 -0
- package/src/native/ffi/build/util.ts +140 -0
- package/src/native/ffi/build.ts +94 -0
- package/src/native/ffi/selftest.ts +11 -718
- package/src/native/ffi/types.ts +25 -0
- package/src/native/ffi.ts +36 -1320
- package/src/native/types/instances.ts +5 -0
- package/src/rust-ffi/packed.ts +38 -0
- package/src/rust-ffi/scalar/factories.ts +4 -0
package/docs/INGRESS.md
CHANGED
|
@@ -83,7 +83,7 @@ provides ready-made route handlers, response builders, and a `Bun.serve`
|
|
|
83
83
|
builder — no need to hand-build responses, header templates, or error bodies.
|
|
84
84
|
|
|
85
85
|
All of these are exported from the package root and are **re-exported from the
|
|
86
|
-
benchmark server** (`bench/servers/ingress-server.ts`), which itself is built
|
|
86
|
+
benchmark server** (`bench/http/servers/ingress-server.ts`), which itself is built
|
|
87
87
|
entirely from them:
|
|
88
88
|
|
|
89
89
|
```ts
|
|
@@ -106,7 +106,7 @@ createIngressServer({
|
|
|
106
106
|
});
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
> **Wire format** (a contract — `bench/load.ts` depends on it):
|
|
109
|
+
> **Wire format** (a contract — `bench/http/load.ts` depends on it):
|
|
110
110
|
> - Success (with `emitMetadataJson: true` — the bench enables it): the body is
|
|
111
111
|
> Rust-generated `{"ok":true,"requestId":"...",...}`. With the DEFAULT
|
|
112
112
|
> `emitMetadataJson: false` the success body is **empty** (lean reads) — serve
|
|
@@ -138,6 +138,9 @@ native options as `createIngressFast` (`trustProxy`, `https`,
|
|
|
138
138
|
| `onResponse` | `(req, result, status, requestId) => void` | — | Hook invoked after a `Response` is produced (metrics/logging) |
|
|
139
139
|
| `onError` | `(req, requestId, error) => void` | — | Hook invoked when the native pipeline fails (silent 500s are otherwise invisible) |
|
|
140
140
|
| `structuredLog` | `boolean` | `false` | Emit one JSON line per request/error (gated by `CASTRUM_LOG_LEVEL`) |
|
|
141
|
+
| `logger` | `StructuredLogger` | — | Custom structured logger (custom stream/extra fields); implies `structuredLog: true` |
|
|
142
|
+
| `maxInFlight` | `number` | `0` (unlimited) | Hard cap on concurrently borrowed zero-copy output buffers (`BufferPool.maxInFlight`); exceeding it fails requests with 500 instead of allocating unbounded temporaries |
|
|
143
|
+
| `zeroCopyTimeoutMs` | `number` | `0` (disabled) | Abandonment guard (ms) for zero-copy responses: unread/cancelled bodies release their pooled buffer and close the stream |
|
|
141
144
|
|
|
142
145
|
Returns an `OptimizedIngressHandler`:
|
|
143
146
|
|
|
@@ -288,7 +291,7 @@ rendering).
|
|
|
288
291
|
> (`createIngressServer`) and Node (`createIngressServerNode`). Raw handlers
|
|
289
292
|
> are served for GET only and skip the ingress pipeline / OPTIONS preflight.
|
|
290
293
|
> See `examples/basic-server.ts` and the bench ingress server
|
|
291
|
-
> (`bench/servers/ingress-server.ts`), which wire `/metrics` + `/healthz`
|
|
294
|
+
> (`bench/http/servers/ingress-server.ts`), which wire `/metrics` + `/healthz`
|
|
292
295
|
> `/readyz` `/livez` end-to-end.
|
|
293
296
|
|
|
294
297
|
**Tracing** — W3C trace-context helpers: `parseTraceParent(header)`,
|
package/docs/NATIVE-ROUTE.md
CHANGED
|
@@ -39,13 +39,37 @@ first-failure-wins in stage order.
|
|
|
39
39
|
- `castrum_route_compile` / `castrum_route_run` / `castrum_route_destroy`
|
|
40
40
|
(C-ABI, needed-size convention, panic-guarded, immutable `&self` run).
|
|
41
41
|
- napi `Route` class (Node / fallback path).
|
|
42
|
+
- **Public castrum surface (`createNativeRoute`, `nativeRouteHandler`)**:
|
|
43
|
+
the stack is now a first-class castrum API, not just the ignex wire.
|
|
44
|
+
- `createNativeRoute(plan)` (`src/ingress/native-route.ts`) compiles a
|
|
45
|
+
route-wire v3 descriptor once and runs each frame in ONE native call —
|
|
46
|
+
`run(query, cookie, body)` / `runFrame(frame)` return the decoded verdict
|
|
47
|
+
(flags + errorCode + query/cookie pairs); `destroy()` frees the handle.
|
|
48
|
+
The pure wire helpers (`encodeRouteDescriptor`, `packRouteFrame`,
|
|
49
|
+
`decodeRouteResult`, layout constants) live in
|
|
50
|
+
`src/ingress/packing/route-wire.ts`.
|
|
51
|
+
- `nativeRouteHandler(plan, responder, opts)` (`src/ingress/routes/native.ts`)
|
|
52
|
+
wraps a compiled route as a `RouteHandler`: extracts the query substring +
|
|
53
|
+
Cookie header, runs the tiny frame, rejects 400/422 on verdict failure,
|
|
54
|
+
and hands the decoded snapshot to the responder for the 2xx.
|
|
55
|
+
- `createIngressRouter`'s `native` route spec wires it into a server (see
|
|
56
|
+
`INGRESS-ROUTER.md`). Measured: ~580ns cheaper per request than the
|
|
57
|
+
full-pipeline responder on a parseQuery+parseCookies route, and **+34%
|
|
58
|
+
RPS at the HTTP level** on the bench server's `/api/native` vs `/api/users`
|
|
59
|
+
(server-bound config, 2000 connections).
|
|
60
|
+
- Trade-off (deliberate): the native stack does NOT do CORS, rate limiting,
|
|
61
|
+
security headers, IP trust, or the castrum metadata envelope — routes that
|
|
62
|
+
need those must use the full pipeline. The lean path is for routes where
|
|
63
|
+
the framework owns the response body and only needs parse + verdict.
|
|
42
64
|
|
|
43
65
|
## Tests / parity
|
|
44
66
|
|
|
45
|
-
- `rust/ingress/native_route.rs` unit tests + `rust/ffi
|
|
46
|
-
- `test/unit/ingress/native-route.test.ts
|
|
47
|
-
- `
|
|
48
|
-
`
|
|
67
|
+
- `rust/ingress/native_route.rs` unit tests + `rust/ffi/` C-ABI tests.
|
|
68
|
+
- `test/unit/ingress/native-route.test.ts` (wire round-trip + lenient parity).
|
|
69
|
+
- `test/unit/ingress/native-route-public.test.ts` (public surface + router
|
|
70
|
+
`native` kind).
|
|
71
|
+
- External parity: ignex's `route-wire.test.ts` /
|
|
72
|
+
`packages/native/test/route.test.ts` (in the `@ignex/native` repo).
|
|
49
73
|
|
|
50
74
|
> **Naming note (flux → ignex)**: the external consumer was historically called
|
|
51
75
|
> `@flux/native` (castrum v0.8.0, guarded by `test/compat/flux-contract.test.ts`).
|
package/docs/REPO_MAP.md
CHANGED
|
@@ -14,14 +14,14 @@ agent editing this repo, [`AGENTS.md`](../AGENTS.md) is the agent-facing map.
|
|
|
14
14
|
|
|
15
15
|
- A **Rust cdylib** (`castrum.<platform>-<arch>.node`, built with napi-rs,
|
|
16
16
|
ALSO exporting `extern "C"` symbols for Bun's `bun:ffi` — see
|
|
17
|
-
`rust/ffi
|
|
17
|
+
`rust/ffi/`) provides the performance-critical primitives (parsers,
|
|
18
18
|
crypto, hashing, JSON, compression, and the HTTP **ingress pipeline**).
|
|
19
19
|
Under Bun, `bun:ffi` is the PRIMARY transport; NAPI is the fallback
|
|
20
20
|
(Node, `CASTRUM_FFI_MODE=napi`, or a failed ffi self-test).
|
|
21
21
|
- **TypeScript** (`src/`) provides the ergonomic public API: the flat `rust.*`
|
|
22
22
|
FFI client and the ingress layer.
|
|
23
23
|
- It ships benchmarks: a **CPU benchmark** (`bench.ts` → `src/bench`) and an
|
|
24
|
-
**HTTP benchmark** (`bench/run-bench.ts` + `bench/servers/*`).
|
|
24
|
+
**HTTP benchmark** (`bench/http/run-bench.ts` + `bench/http/servers/*`).
|
|
25
25
|
|
|
26
26
|
Bun is the primary runtime; Node.js ≥20.3 is supported via a compiled ESM entry
|
|
27
27
|
(`dist/`).
|
|
@@ -37,7 +37,7 @@ Bun is the primary runtime; Node.js ≥20.3 is supported via a compiled ESM entr
|
|
|
37
37
|
| Build Rust addon (LOCAL max perf) | `bun run build:perf` | x86-64-v3 + AVX2 — **never for publish** |
|
|
38
38
|
| Build Rust addon (debug) | `bun run build:debug` | |
|
|
39
39
|
| Build compiled JS entry (Node) | `bun run build:js` | bundle + types → `dist/` (gitignored) |
|
|
40
|
-
| TS unit tests | `bun test` | ~
|
|
40
|
+
| TS unit tests | `bun test` | ~760 tests, `test/unit/**` |
|
|
41
41
|
| Rust unit tests | `bun run test:rust` | `cargo test`, ~440 tests |
|
|
42
42
|
| Node smoke tests | `bun run test:node` | explicit `.mjs` paths (Node 24 rejects dir arg) |
|
|
43
43
|
| Installed-tarball e2e | `bun run verify:install` | pack → install → import from `node_modules` |
|
|
@@ -45,6 +45,8 @@ Bun is the primary runtime; Node.js ≥20.3 is supported via a compiled ESM entr
|
|
|
45
45
|
| CPU benchmark + correctness | `bun run check` | == `bun bench.ts`; writes `bench/results/cpu/` |
|
|
46
46
|
| HTTP bench (all servers) | `bun run bench:http` | |
|
|
47
47
|
| HTTP smoke (fast sanity) | `bun run bench:http:smoke` | **the wire-format guard** (CI-gated) |
|
|
48
|
+
| FFI transport benches | `bun run bench:ffi` / `bench:ffi:load` / `bench:ffi:public` / `bench:ffi:workers` / `bench:margin` | `bench/ffi/` |
|
|
49
|
+
| Ingress cost benches | `bun run bench:ingress-cost` / `bench:ingress-cost:post` / `bench:router` | `bench/cost/` |
|
|
48
50
|
| Startup bench | `bun run bench:startup` | import + first-call timing |
|
|
49
51
|
| JS lint (Biome) | `bun run lint` / `lint:fix` | `bunx biome check [--write] .` |
|
|
50
52
|
| JS format (Biome) | `bun run format` | `bunx biome format --write .` |
|
|
@@ -71,9 +73,13 @@ src/
|
|
|
71
73
|
types.ts NativeAddon module-surface interface (mirrors index.d.ts).
|
|
72
74
|
types/instances.ts Per-class INSTANCE types (Ingress, SchemaValidator, JwtSigner, ...) + result types.
|
|
73
75
|
loader.ts Addon path resolution (multi-root) + getAddon()/lazyAddon().
|
|
74
|
-
ffi.ts bun:ffi C-ABI
|
|
75
|
-
CASTRUM_FFI_MODE gating
|
|
76
|
-
ffi/
|
|
76
|
+
ffi.ts bun:ffi C-ABI transport core (Bun-only): mode resolution, buffer/buffer_length
|
|
77
|
+
probe, bind() orchestration (self-test + CASTRUM_FFI_MODE gating + napi fallback).
|
|
78
|
+
ffi/ Decomposed bind surface: types.ts (BunFFI interface) + constants.ts +
|
|
79
|
+
selftest.ts (thin aggregator) + build.ts (orchestrator) +
|
|
80
|
+
build/{util,codecs,compress,parse,instances}.ts — per-domain method builders
|
|
81
|
+
EACH with its self-test* function, so a new castrum_* symbol is bound AND
|
|
82
|
+
self-tested in one domain file.
|
|
77
83
|
index.ts Barrel.
|
|
78
84
|
runtime/ THE RUNTIME ADAPTER seam (Bun/Node) — selected once at load.
|
|
79
85
|
detect.ts Cached runtime detection — the ONLY place `typeof Bun` is checked.
|
|
@@ -108,21 +114,31 @@ src/
|
|
|
108
114
|
buffer-pool.ts Generic reusable byte-buffer pool (pooled ingress output).
|
|
109
115
|
response.ts pooledBodyResponse (releases the pool on body consume).
|
|
110
116
|
log.ts createStructuredLogger (CASTRUM_LOG_LEVEL-gated JSON lines).
|
|
117
|
+
metrics.ts Zero-dep metrics registry (counters/gauges/histograms + Prometheus render).
|
|
118
|
+
trace.ts W3C traceparent/span-id helpers.
|
|
119
|
+
env.ts Centralized env-var resolution (CASTRUM_* + NAPI_RS_NATIVE_LIBRARY_PATH).
|
|
120
|
+
proven.ts Baked benchmark-proven op selection (PROVEN_SELECTION — pure data).
|
|
121
|
+
loader/ Higher-order-function data loader (HFC) over the curated op set.
|
|
122
|
+
index.ts Dispatch core + factory (scalar/bulk dispatch, coalescing, LRU cache).
|
|
123
|
+
ops.ts Op registry (LOADER_OPS) + type tower; state.ts / cost.ts / batch.ts / types.ts.
|
|
111
124
|
ingress/ The HTTP ingress pipeline (two paths — see §4.2).
|
|
112
125
|
fast.ts PATH 1: createIngressFast (packed-input via handleRequestPacked).
|
|
113
126
|
handlers.ts PATH 2: createIngressHandler (JS-packs the frame via IngressInputPacker
|
|
114
|
-
+ gatherRawHeadersPacked → handleRequestPacked)
|
|
115
|
-
|
|
127
|
+
+ gatherRawHeadersPacked → handleRequestPacked); response builders
|
|
128
|
+
extracted to response/baked-response.ts.
|
|
129
|
+
index.ts Pure public barrel (all re-exports).
|
|
130
|
+
sync.ts createIngressSync / createIngress convenience factories (path 1 wrapper).
|
|
116
131
|
shared.ts JS constants + buildHeaderPlan + assertSyncCallback (shared by both paths).
|
|
117
132
|
constants.ts Binary-layout constants read from Rust at runtime (SINGLE SOURCE).
|
|
118
133
|
status.ts / errors.ts / options.ts / types.ts / body.ts / context.ts
|
|
119
134
|
packing/ header-packing.ts, input-packer.ts, gather-raw-headers.ts
|
|
120
|
-
headers/ cors.ts, hsts.ts, fast-templates.ts, baked-templates.ts
|
|
135
|
+
headers/ cors.ts, hsts.ts, security.ts (baked security merge), fast-templates.ts, baked-templates.ts
|
|
121
136
|
decode/ fast-result.ts, baked-result.ts (TWO decoders — see §4.2)
|
|
122
|
-
response/ terminal.ts, error-bodies.ts
|
|
123
|
-
routes/ read/head/json-write/echo/fallback factories + common.ts
|
|
137
|
+
response/ terminal.ts, error-bodies.ts, baked-response.ts (pre-baked response builders)
|
|
138
|
+
routes/ read/head/json-write/echo/fallback factories + common.ts + responder.ts
|
|
124
139
|
server.ts createIngressServer (Bun.serve) + buildRouteHandlers + gracefulShutdown.
|
|
125
140
|
server-node.ts createIngressServerNode (node:http adapter, same route handlers).
|
|
141
|
+
path-matcher.ts buildPathMatcher + PathMatch + safeDecode (shared by server-node + router).
|
|
126
142
|
integration/ Framework integration (wraps path 2 — createIngressHandler).
|
|
127
143
|
pipeline.ts createPipeline (framework-agnostic request stage for Hono/Elysia/Bun.serve).
|
|
128
144
|
websocket.ts createWebSocketUpgrade (RFC 6455 101 handshake + subprotocol).
|
|
@@ -135,22 +151,33 @@ src/
|
|
|
135
151
|
```
|
|
136
152
|
rust/ ONE cdylib crate (Cargo [lib] → lib.rs).
|
|
137
153
|
lib.rs Declaration hub + module map + crate docs.
|
|
138
|
-
|
|
154
|
+
selection.rs Rust-side op selection metadata (mirrors src/selection.json).
|
|
155
|
+
util/ Shared infrastructure: bytes, packed, batch/ (api.rs + core.rs + tests.rs),
|
|
156
|
+
threadpool, validation.
|
|
139
157
|
http/ HTTP wire formats & parsing: headers, method, http/cookie/query parsers,
|
|
140
|
-
form, media_type, url_codec, url_join, etag, accept,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
form, media_type, url_codec, url_join, http_date, etag, accept,
|
|
159
|
+
mime_lookup, multipart.
|
|
160
|
+
crypto/ Auth & hashing: hmac_sha256, cookie_sign, csrf, jwt/ (token.rs + api.rs +
|
|
161
|
+
tests.rs), aead, argon2, base64, ed25519, hashing (fnv/crc32/xxh3),
|
|
162
|
+
random_token.
|
|
163
|
+
json/ JSON & schema: json_ops, json_ser, json/patch/ (pointer.rs + ops.rs +
|
|
164
|
+
engine.rs + api.rs + tests.rs), json_schema (napi),
|
|
144
165
|
fast_schema/ (zero-DOM engine — pure, no napi).
|
|
145
166
|
payload/ Output & streaming: compress, sse, ws_frames, websocket, template.
|
|
146
|
-
ingress/ THE ingress pipeline:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
ingress/ THE ingress pipeline: api.rs (Ingress/IngressInner napi class) + mod.rs
|
|
168
|
+
(declaration hub), pipeline.rs (core 8-stage), native_route.rs (per-route
|
|
169
|
+
native stack), options/time/packed, cors, proxy, ip_trust, rate_limit,
|
|
170
|
+
terminal, output.rs (single numeric layout source),
|
|
171
|
+
ingress_constants.rs (napi projection).
|
|
172
|
+
ffi/ #[no_mangle] extern "C" exports (85 castrum_* symbols — 77 direct + 4
|
|
150
173
|
validator_c_abi! + 4 compress_to_out!; parity guarded by
|
|
151
|
-
test/unit/
|
|
174
|
+
test/unit/native/ffi-symbol-parity.test.ts) for Bun's bun:ffi
|
|
152
175
|
primary transport, incl. castrum_ingress_layout (the layout blob).
|
|
153
|
-
|
|
176
|
+
mod.rs (module map) + util.rs (panic_guard / HMAC cache / cstring helpers)
|
|
177
|
+
+ per-domain wrappers (hashing/validators/crypto/jwt/http/payload/json/
|
|
178
|
+
rate_limit/ingress/route/probe) + tests.rs.
|
|
179
|
+
panic_safety.rs / test_support.rs Cross-module fuzz + shared #[cfg(test)] helpers.
|
|
180
|
+
proptest_suite.rs Property-based adversarial-parser tests (dev-dep proptest).
|
|
154
181
|
```
|
|
155
182
|
|
|
156
183
|
### Generated / ignored (do NOT edit)
|
|
@@ -160,7 +187,7 @@ rust/ ONE cdylib crate (Cargo [lib] → lib.rs).
|
|
|
160
187
|
| `dist/` | Compiled ESM entry + types for Node | Built by `build:js`; gitignored |
|
|
161
188
|
| `index.js` / `index.d.ts` | NAPI-RS generated loader/declarations | Regenerated by `napi build`; gitignored |
|
|
162
189
|
| `castrum.<platform>-<arch>.node` | The native addon binary | Built locally; gitignored |
|
|
163
|
-
| `bench/results/` | Bench reports (cpu/, http/) | Generated; gitignored |
|
|
190
|
+
| `bench/results/` | Bench reports (cpu/, per-server http/, ffi-margin/, router/, startup/, autocannon/) | Generated; gitignored |
|
|
164
191
|
| `target/` | Cargo build output | gitignored |
|
|
165
192
|
| `node_modules/` | Deps | gitignored |
|
|
166
193
|
| `artifacts/` | CI publish staging dir | gitignored |
|
|
@@ -172,7 +199,7 @@ rust/ ONE cdylib crate (Cargo [lib] → lib.rs).
|
|
|
172
199
|
### 4.1 Constants never drift (single numeric source)
|
|
173
200
|
`rust/ingress/output.rs` defines every ingress layout constant once → projected
|
|
174
201
|
to JS two ways: `rust/ingress/ingress_constants.rs` via `#[napi] const` (NAPI)
|
|
175
|
-
AND `rust/ffi
|
|
202
|
+
AND `rust/ffi/` `IngressLayout` via `castrum_ingress_layout` (C ABI, primary
|
|
176
203
|
on Bun) → `src/ingress/constants.ts` reads them at runtime (ffi blob on Bun,
|
|
177
204
|
napi on Node). Drift is pinned by the Rust unit test
|
|
178
205
|
`ingress_layout_c_abi_matches_output_source` and the bun:ffi bind-time
|
|
@@ -191,7 +218,7 @@ self-test. **Never hardcode a layout value** — if you change the layout, chang
|
|
|
191
218
|
`{"ok":true,...,"requestId":...}` (success) / `{"ok":false,"error":{code,
|
|
192
219
|
message}}` (errors) with `ratelimit-*` headers.
|
|
193
220
|
|
|
194
|
-
These wire formats are a **benchmark contract** (`bench/load.ts` validates
|
|
221
|
+
These wire formats are a **benchmark contract** (`bench/http/load.ts` validates
|
|
195
222
|
them) — they are intentionally NOT unified. The two decoders
|
|
196
223
|
(`decode/fast-result.ts` vs `decode/baked-result.ts`) and the two header-template
|
|
197
224
|
builders (`headers/fast-templates.ts` vs `headers/baked-templates.ts`) exist for
|
|
@@ -235,16 +262,19 @@ consumed.
|
|
|
235
262
|
|
|
236
263
|
## 5. Testing & benchmarks
|
|
237
264
|
|
|
238
|
-
- **TS**: `bun test` → `test/unit/**` (`
|
|
265
|
+
- **TS**: `bun test` → `test/unit/**` (`native/`, `rust-ffi/`, `contract/`,
|
|
266
|
+
`ingress/`, `shared/`, `integration/`) + `test/compat/` + `test/property/`.
|
|
267
|
+
See `test/README.md` for the intended split.
|
|
239
268
|
- **Rust**: `cargo test` → per-module `#[cfg(test)] mod tests` + cross-module
|
|
240
|
-
`rust/
|
|
269
|
+
`rust/panic_safety.rs` (fuzz) + `rust/proptest_suite.rs`.
|
|
241
270
|
- **Node**: `test/integration/node-smoke.test.mjs` + `node-enterprise.test.mjs`
|
|
242
271
|
via `node --test` (run with `bun run test:node`).
|
|
243
272
|
- **CPU bench**: `bun run check` (correctness checks + comparisons). Sub-µs ops
|
|
244
273
|
are batched (`CASTRUM_BENCH_BATCH_SIZE`). Report → `bench/results/cpu/`.
|
|
245
|
-
- **HTTP bench**: `bench/run-bench.ts` + `bench/load.ts` (load generator that
|
|
246
|
-
validates response SHAPE) + `bench/servers/{bun,elysia,ingress}-server.ts`.
|
|
247
|
-
`bench:http:smoke` is the fast CI wire-format gate.
|
|
274
|
+
- **HTTP bench**: `bench/http/run-bench.ts` + `bench/http/load.ts` (load generator that
|
|
275
|
+
validates response SHAPE) + `bench/http/servers/{bun,elysia,ingress,router}-server.ts`.
|
|
276
|
+
`bench:http:smoke` is the fast CI wire-format gate. FFI-transport benches live in
|
|
277
|
+
`bench/ffi/`; per-request cost benches in `bench/cost/` (see `bench/README.md`).
|
|
248
278
|
|
|
249
279
|
---
|
|
250
280
|
|
|
@@ -20,7 +20,7 @@ They also use separate result decoders (`FastIngressResult` vs
|
|
|
20
20
|
|
|
21
21
|
## Decision
|
|
22
22
|
|
|
23
|
-
**Do not unify them.** The HTTP benchmark load generator (`bench/load.ts`)
|
|
23
|
+
**Do not unify them.** The HTTP benchmark load generator (`bench/http/load.ts`)
|
|
24
24
|
requires `ok === true` + `requestId: string` on success and `error.code` /
|
|
25
25
|
`error.message` on errors — i.e. path 2's format. The two formats are a
|
|
26
26
|
**benchmark contract**: the benchmark server uses path 2, and changing its
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Local Development with the Core Projects — `bun link`
|
|
2
|
+
|
|
3
|
+
> **Scope**: maintainers and AI agents working across the IgnEX core stack.
|
|
4
|
+
> Application developers consume published versions from npm and do **not**
|
|
5
|
+
> need this file.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
The IgnEX core packages live side-by-side, one directory back from this repo,
|
|
10
|
+
in `/home/adeel/poc/`. When a change in this repo must be tested against the
|
|
11
|
+
local source of a core project (or a core change must be tested against this
|
|
12
|
+
repo), use `bun link` so the consumer resolves the local directory instead of
|
|
13
|
+
the npm registry. This is the supported Bun ≥ 1.4 (Rust-based runtime)
|
|
14
|
+
local-development mechanism
|
|
15
|
+
([`bun link` docs](https://bun.com/docs/cli/link),
|
|
16
|
+
[bun.com/blog/bun-v1.4](https://bun.com/blog/bun-v1.4)).
|
|
17
|
+
|
|
18
|
+
This workflow is **only for maintainers and AI agents working with the core
|
|
19
|
+
projects**. CI and release pipelines always resolve from the registry; local
|
|
20
|
+
links are a development-only convenience.
|
|
21
|
+
|
|
22
|
+
## Core packages (one directory back)
|
|
23
|
+
|
|
24
|
+
| Repo (`/home/adeel/poc/`) | Package(s) | `bun link` name |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `ignus` — ignex monorepo, workspaces `packages/*` | `@ignex/core`, `@ignex/cli`, `@ignex/compiler`, `@ignex/native`, `@ignex/shared`, `@ignex/mcp`, `@ignex/app`, `create-ignex` | run `bun link` inside each package dir |
|
|
27
|
+
| `bun-rust-runtime-bench` | `castrum` (Rust addon `castrum.<platform>-<arch>.node`) | `castrum` |
|
|
28
|
+
| `ignex-mongodb` | `@ignex/ninox` | `@ignex/ninox` |
|
|
29
|
+
| `ignex-nova` | `@ignex/nova` | `@ignex/nova` |
|
|
30
|
+
|
|
31
|
+
Known cross-repo edges (verify with `grep` in `package.json` before assuming):
|
|
32
|
+
|
|
33
|
+
- `@ignex/native` (in `ignus/packages/native`) depends on `castrum` — link
|
|
34
|
+
`castrum` there when changing the addon wire (`createNativeRoute`).
|
|
35
|
+
- `@ignex/core` (in `ignus/packages/core`) optionally depends on
|
|
36
|
+
`@ignex/nova`; `@ignex/app` depends on `@ignex/core`, `@ignex/cli`, and
|
|
37
|
+
`@ignex/ninox`.
|
|
38
|
+
|
|
39
|
+
## How to link
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1. Register the core package (once per machine, from the core repo):
|
|
43
|
+
cd /home/adeel/poc/ignex-mongodb
|
|
44
|
+
bun link # → Success! Registered "@ignex/ninox"
|
|
45
|
+
|
|
46
|
+
# 2. Link it into the consumer project:
|
|
47
|
+
cd /home/adeel/poc/ignex-app
|
|
48
|
+
bun link @ignex/ninox # symlinks node_modules/@ignex/ninox → ../ignex-mongodb
|
|
49
|
+
bun link @ignex/ninox --save # also writes "link:@ignex/ninox" into package.json deps
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `bun link` (no args) in a package directory registers that package globally
|
|
53
|
+
for this user.
|
|
54
|
+
- `bun link <name>` in a consumer creates a symlink in the consumer's
|
|
55
|
+
`node_modules` pointing at the registered directory. `--save` additionally
|
|
56
|
+
records `"<name>": "link:<name>"` in `package.json` dependencies.
|
|
57
|
+
- Unregister a package: `bun unlink` from the core repo dir.
|
|
58
|
+
- Return to registry versions: remove the `link:` entry (or `bun unlink
|
|
59
|
+
<name>` in the consumer) and `bun install`.
|
|
60
|
+
|
|
61
|
+
## Rust-core caveats (castrum & @ignex/nova)
|
|
62
|
+
|
|
63
|
+
Both `castrum` and `@ignex/nova` ship a Rust cdylib. After changing Rust
|
|
64
|
+
source, rebuild the addon **before** linking or using:
|
|
65
|
+
|
|
66
|
+
- `castrum`: `bun run build` (release `napi build`) or `bun run build:debug`.
|
|
67
|
+
Under Bun the addon is also called through `bun:ffi` from the same cdylib.
|
|
68
|
+
- `@ignex/nova`: `bun run build:rust`
|
|
69
|
+
(`cargo build --release --manifest-path rust/Cargo.toml`), or rely on
|
|
70
|
+
`prepack`/`prebuild` to stage `prebuilds/<platform>-<arch>/`.
|
|
71
|
+
|
|
72
|
+
A stale `.node`/`.so` silently serves old behavior — rebuild, then re-test.
|
|
73
|
+
`@ignex/nova` additionally fails its bind-time self-test on schema/wire-version
|
|
74
|
+
mismatch (`IGNEX_FFI_PATH` can point at a specific build); `castrum` falls back
|
|
75
|
+
to the napi transport when the `bun:ffi` self-test fails.
|
|
76
|
+
|
|
77
|
+
## Never publish from a linked tree
|
|
78
|
+
|
|
79
|
+
Publishing a consumer whose dependencies are `link:` entries ships symlinks,
|
|
80
|
+
not packages. Releases always run against registry versions (`prepublishOnly` /
|
|
81
|
+
CI re-verify with a clean install). Keep `bun link` strictly local.
|