castrum 0.7.0

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 (86) hide show
  1. package/Cargo.toml +73 -0
  2. package/README.md +432 -0
  3. package/bench.ts +3 -0
  4. package/castrum.linux-x64-gnu.node +0 -0
  5. package/index.ts +24 -0
  6. package/package.json +94 -0
  7. package/rust/batch.rs +195 -0
  8. package/rust/cookie_parser.rs +70 -0
  9. package/rust/cors.rs +263 -0
  10. package/rust/hashing.rs +164 -0
  11. package/rust/headers.rs +234 -0
  12. package/rust/hmac_sha256.rs +113 -0
  13. package/rust/http_parser.rs +132 -0
  14. package/rust/ingress.rs +616 -0
  15. package/rust/ingress_constants.rs +89 -0
  16. package/rust/ip_trust.rs +185 -0
  17. package/rust/json_ops.rs +66 -0
  18. package/rust/json_patch_ops.rs +25 -0
  19. package/rust/json_schema.rs +151 -0
  20. package/rust/json_ser.rs +429 -0
  21. package/rust/lib.rs +48 -0
  22. package/rust/method.rs +141 -0
  23. package/rust/mime_lookup.rs +76 -0
  24. package/rust/output.rs +238 -0
  25. package/rust/proxy.rs +90 -0
  26. package/rust/query_parser.rs +141 -0
  27. package/rust/random_token.rs +52 -0
  28. package/rust/rate_limit.rs +203 -0
  29. package/rust/terminal.rs +95 -0
  30. package/rust/unit_tests.rs +843 -0
  31. package/rust/url_codec.rs +133 -0
  32. package/rust/util.rs +537 -0
  33. package/rust/validation.rs +107 -0
  34. package/rust/websocket.rs +23 -0
  35. package/src/baseline/index.ts +12 -0
  36. package/src/baseline/tasks/cookie.ts +22 -0
  37. package/src/baseline/tasks/hashing.ts +18 -0
  38. package/src/baseline/tasks/hmac.ts +38 -0
  39. package/src/baseline/tasks/http.ts +62 -0
  40. package/src/baseline/tasks/json-patch.ts +12 -0
  41. package/src/baseline/tasks/json.ts +50 -0
  42. package/src/baseline/tasks/mime.ts +5 -0
  43. package/src/baseline/tasks/query.ts +14 -0
  44. package/src/baseline/tasks/token.ts +15 -0
  45. package/src/baseline/tasks/url.ts +11 -0
  46. package/src/baseline/tasks/validation.ts +19 -0
  47. package/src/baseline/tasks/websocket.ts +14 -0
  48. package/src/bench/assert.ts +70 -0
  49. package/src/bench/checks.ts +227 -0
  50. package/src/bench/checksum.ts +27 -0
  51. package/src/bench/comparisons.ts +70 -0
  52. package/src/bench/concurrent-worker.ts +135 -0
  53. package/src/bench/fixtures.ts +233 -0
  54. package/src/bench/index.ts +2 -0
  55. package/src/bench/measure.ts +177 -0
  56. package/src/bench/now.ts +3 -0
  57. package/src/bench/report.ts +79 -0
  58. package/src/bench/run.ts +57 -0
  59. package/src/bench/tasks/complex.ts +232 -0
  60. package/src/bench/tasks/concurrent.ts +213 -0
  61. package/src/bench/tasks/cookie.ts +44 -0
  62. package/src/bench/tasks/hashing.ts +33 -0
  63. package/src/bench/tasks/hmac.ts +36 -0
  64. package/src/bench/tasks/http.ts +50 -0
  65. package/src/bench/tasks/index.ts +52 -0
  66. package/src/bench/tasks/json-patch.ts +21 -0
  67. package/src/bench/tasks/json.ts +33 -0
  68. package/src/bench/tasks/mime.ts +21 -0
  69. package/src/bench/tasks/query.ts +44 -0
  70. package/src/bench/tasks/stress.ts +142 -0
  71. package/src/bench/tasks/token.ts +20 -0
  72. package/src/bench/tasks/url.ts +34 -0
  73. package/src/bench/tasks/validation.ts +57 -0
  74. package/src/bench/tasks/websocket.ts +21 -0
  75. package/src/bench/types.ts +60 -0
  76. package/src/data/json-rows.ts +33 -0
  77. package/src/ingress/constants.ts +56 -0
  78. package/src/ingress/fast.ts +1082 -0
  79. package/src/ingress/handlers.ts +1202 -0
  80. package/src/ingress/index.ts +457 -0
  81. package/src/ingress/packed-input.ts +59 -0
  82. package/src/native/index.ts +207 -0
  83. package/src/rust-ffi/index.ts +555 -0
  84. package/src/shared/bytes.ts +36 -0
  85. package/src/shared/json.ts +18 -0
  86. package/src/shared/packed.ts +316 -0
@@ -0,0 +1,20 @@
1
+ import * as native from "../../baseline";
2
+ import { rust } from "../../rust-ffi";
3
+ import type { BenchTask } from "../types";
4
+
5
+ export function tokenTasks(): BenchTask[] {
6
+ return [
7
+ {
8
+ name: "native:random_token",
9
+ run: () => native.nativeRandomToken(32).byteLength,
10
+ iterations: 1000,
11
+ warmup: 100,
12
+ },
13
+ {
14
+ name: "rust:random_token",
15
+ run: () => rust.randomToken(32).byteLength,
16
+ iterations: 1000,
17
+ warmup: 100,
18
+ },
19
+ ];
20
+ }
@@ -0,0 +1,34 @@
1
+ import * as native from "../../baseline";
2
+ import { rust } from "../../rust-ffi";
3
+ import type { BenchFixtures } from "../fixtures";
4
+ import type { BenchTask } from "../types";
5
+
6
+ export function urlTasks(f: BenchFixtures): BenchTask[] {
7
+ return [
8
+ {
9
+ name: "native:url_encode",
10
+ run: () => native.nativeUrlEncode("hello world & foo=bar").length,
11
+ iterations: 1000,
12
+ warmup: 100,
13
+ },
14
+ {
15
+ name: "rust:url_encode",
16
+ run: () => rust.urlEncode(f.urlEncodeInput).byteLength,
17
+ iterations: 1000,
18
+ warmup: 100,
19
+ },
20
+ {
21
+ name: "native:url_decode",
22
+ run: () =>
23
+ native.nativeUrlDecode("hello%20world%20%26%20foo%3Dbar").length,
24
+ iterations: 1000,
25
+ warmup: 100,
26
+ },
27
+ {
28
+ name: "rust:url_decode",
29
+ run: () => rust.urlDecode(f.urlDecodeInput).byteLength,
30
+ iterations: 1000,
31
+ warmup: 100,
32
+ },
33
+ ];
34
+ }
@@ -0,0 +1,57 @@
1
+ import * as native from "../../baseline";
2
+ import { rust } from "../../rust-ffi";
3
+ import type { BenchFixtures } from "../fixtures";
4
+ import type { BenchTask } from "../types";
5
+
6
+ export function validationTasks(f: BenchFixtures): BenchTask[] {
7
+ return [
8
+ {
9
+ name: "native:validate_email",
10
+ run: () => (native.nativeValidateEmail(f.emailOk) ? 1 : 0),
11
+ iterations: 1000,
12
+ warmup: 100,
13
+ },
14
+ {
15
+ name: "rust:validate_email",
16
+ run: () => rust.validateEmail(f.emailOk),
17
+ iterations: 1000,
18
+ warmup: 100,
19
+ },
20
+ {
21
+ name: "native:validate_uuid",
22
+ run: () => (native.nativeValidateUuid(f.uuidOk) ? 1 : 0),
23
+ iterations: 1000,
24
+ warmup: 100,
25
+ },
26
+ {
27
+ name: "rust:validate_uuid",
28
+ run: () => rust.validateUuid(f.uuidOk),
29
+ iterations: 1000,
30
+ warmup: 100,
31
+ },
32
+ {
33
+ name: "native:validate_ipv4",
34
+ run: () => (native.nativeValidateIpv4(f.ipv4Ok) ? 1 : 0),
35
+ iterations: 1000,
36
+ warmup: 100,
37
+ },
38
+ {
39
+ name: "rust:validate_ipv4",
40
+ run: () => rust.validateIpv4(f.ipv4Ok),
41
+ iterations: 1000,
42
+ warmup: 100,
43
+ },
44
+ {
45
+ name: "native:validate_ipv6",
46
+ run: () => (native.nativeValidateIpv6(f.ipv6Ok) ? 1 : 0),
47
+ iterations: 1000,
48
+ warmup: 100,
49
+ },
50
+ {
51
+ name: "rust:validate_ipv6",
52
+ run: () => rust.validateIpv6(f.ipv6Ok),
53
+ iterations: 1000,
54
+ warmup: 100,
55
+ },
56
+ ];
57
+ }
@@ -0,0 +1,21 @@
1
+ import * as native from "../../baseline";
2
+ import { rust } from "../../rust-ffi";
3
+ import type { BenchFixtures } from "../fixtures";
4
+ import type { BenchTask } from "../types";
5
+
6
+ export function websocketTasks(f: BenchFixtures): BenchTask[] {
7
+ return [
8
+ {
9
+ name: "native:ws_accept_key",
10
+ run: () => native.nativeWsAcceptKey(f.wsKey).byteLength,
11
+ iterations: 1000,
12
+ warmup: 100,
13
+ },
14
+ {
15
+ name: "rust:ws_accept_key",
16
+ run: () => rust.wsAcceptKey(f.wsKeyBytes).byteLength,
17
+ iterations: 1000,
18
+ warmup: 100,
19
+ },
20
+ ];
21
+ }
@@ -0,0 +1,60 @@
1
+ export interface BenchResult {
2
+ name: string;
3
+ iterations: number;
4
+ avgMs: number;
5
+ p50Ms: number;
6
+ p95Ms: number;
7
+ opsPerSec: number;
8
+ checksum: string;
9
+ }
10
+
11
+ export interface ConcurrentBenchResult {
12
+ name: string;
13
+ iterations: number;
14
+ concurrency: number;
15
+ totalOps: number;
16
+ totalMs: number;
17
+ avgMs: number;
18
+ p50Ms: number;
19
+ p95Ms: number;
20
+ opsPerSec: number;
21
+ checksum: string;
22
+ }
23
+
24
+ export interface StressBenchResult {
25
+ name: string;
26
+ durationMs: number;
27
+ totalOps: number;
28
+ opsPerSec: number;
29
+ avgMs: number;
30
+ checksum: string;
31
+ }
32
+
33
+ export interface BenchTask {
34
+ name: string;
35
+ run: () => unknown;
36
+ iterations: number;
37
+ warmup: number;
38
+ }
39
+
40
+ export interface ConcurrentBenchTask {
41
+ name: string;
42
+ op: string;
43
+ payload: unknown;
44
+ concurrency: number;
45
+ iterationsPerSlot: number;
46
+ warmupPerSlot: number;
47
+ }
48
+
49
+ export interface StressBenchTask {
50
+ name: string;
51
+ run: () => unknown;
52
+ durationMs: number;
53
+ warmupMs: number;
54
+ }
55
+
56
+ export interface ComparisonReport {
57
+ label: string;
58
+ nativeName: string;
59
+ rustName: string;
60
+ }
@@ -0,0 +1,33 @@
1
+ import { encoder } from "../shared/bytes";
2
+
3
+ export interface JsonRowNested {
4
+ version: number;
5
+ createdAt: string;
6
+ }
7
+
8
+ export interface JsonRow {
9
+ id: number;
10
+ name: string;
11
+ active: boolean;
12
+ score: number;
13
+ tags: string[];
14
+ nested: JsonRowNested;
15
+ }
16
+
17
+ export function createJsonRows(rows: number): JsonRow[] {
18
+ return Array.from({ length: rows }, (_, i) => ({
19
+ id: i,
20
+ name: `user_${i}`,
21
+ active: i % 2 === 0,
22
+ score: i * 1.25,
23
+ tags: ["alpha", "beta", "gamma"],
24
+ nested: {
25
+ version: i % 10,
26
+ createdAt: "2026-01-01T00:00:00Z",
27
+ },
28
+ }));
29
+ }
30
+
31
+ export function jsonRowsBytes(rows: number): Uint8Array {
32
+ return encoder.encode(JSON.stringify(createJsonRows(rows)));
33
+ }
@@ -0,0 +1,56 @@
1
+ // src/ingress/constants.ts — SINGLE SOURCE OF TRUTH for ingress binary layout constants
2
+ // These are read from the Rust native addon to eliminate duplication across:
3
+ // - src/ingress/index.ts
4
+ // - bench/servers/ingress-server.ts
5
+ // - Any future consumers
6
+ //
7
+ // IMPORTANT: Do NOT hardcode these values. They come from Rust's ingress_constants.rs
8
+ // and are typed on the NativeAddon interface (src/native/index.ts).
9
+
10
+ import addon from "../native";
11
+
12
+ // ── Output buffer layout ───────────────────────────────────
13
+ export const OUT_VERDICT: number = addon.INGRESS_OUT_VERDICT;
14
+ export const OUT_ERROR_CODE: number = addon.INGRESS_OUT_ERROR_CODE;
15
+ export const OUT_STATUS: number = addon.INGRESS_OUT_STATUS;
16
+ export const OUT_FLAGS: number = addon.INGRESS_OUT_FLAGS;
17
+ export const OUT_RATE_LIMIT: number = addon.INGRESS_OUT_RATE_LIMIT;
18
+ export const OUT_RATE_REMAINING: number = addon.INGRESS_OUT_RATE_REMAINING;
19
+ export const OUT_RATE_RESET: number = addon.INGRESS_OUT_RATE_RESET;
20
+ export const OUT_RETRY_AFTER: number = addon.INGRESS_OUT_RETRY_AFTER;
21
+ export const OUT_COOKIES_JSON_LEN: number = addon.INGRESS_OUT_COOKIES_JSON_LEN;
22
+ export const OUT_QUERY_JSON_LEN: number = addon.INGRESS_OUT_QUERY_JSON_LEN;
23
+ export const OUT_HEADER_VARIANT: number = addon.INGRESS_OUT_HEADER_VARIANT;
24
+ export const OUT_BODY_JSON_LEN: number = addon.INGRESS_OUT_BODY_JSON_LEN;
25
+ export const OUT_DATA_START: number = addon.INGRESS_OUT_DATA_START;
26
+
27
+ // ── Flags ──────────────────────────────────────────────────
28
+ export const FLAG_HAS_COOKIES: number = addon.INGRESS_FLAG_HAS_COOKIES;
29
+ export const FLAG_HAS_QUERY: number = addon.INGRESS_FLAG_HAS_QUERY;
30
+ export const FLAG_BODY_VALID_JSON: number = addon.INGRESS_FLAG_BODY_VALID_JSON;
31
+ export const FLAG_SCHEMA_VALID: number = addon.INGRESS_FLAG_SCHEMA_VALID;
32
+ export const FLAG_CORS_ALLOWED: number = addon.INGRESS_FLAG_CORS_ALLOWED;
33
+ export const FLAG_IS_PREFLIGHT: number = addon.INGRESS_FLAG_IS_PREFLIGHT;
34
+ export const FLAG_RATE_LIMITED: number = addon.INGRESS_FLAG_RATE_LIMITED;
35
+ export const FLAG_HTTPS: number = addon.INGRESS_FLAG_HTTPS;
36
+ export const FLAG_TRUSTED_PROXY: number = addon.INGRESS_FLAG_TRUSTED_PROXY;
37
+ export const FLAG_BODY_TRUNCATED: number = addon.INGRESS_FLAG_BODY_TRUNCATED;
38
+
39
+ // ── Header variant bits ────────────────────────────────────
40
+ export const HV_JSON: number = addon.INGRESS_HV_JSON;
41
+ export const HV_CORS_SIMPLE: number = addon.INGRESS_HV_CORS_SIMPLE;
42
+ export const HV_CORS_PREFLIGHT: number = addon.INGRESS_HV_CORS_PREFLIGHT;
43
+ export const HV_RATE_ACTIVE: number = addon.INGRESS_HV_RATE_ACTIVE;
44
+ export const HV_RATE_LIMITED: number = addon.INGRESS_HV_RATE_LIMITED;
45
+ export const HV_COUNT: number = addon.INGRESS_HV_COUNT;
46
+
47
+ // ── Error codes ────────────────────────────────────────────
48
+ export const ERR_CODE_NONE: number = addon.INGRESS_ERR_NONE;
49
+ export const ERR_CODE_CORS_PREFLIGHT: number = addon.INGRESS_ERR_CORS_PREFLIGHT;
50
+ export const ERR_CODE_RATE_LIMITED: number = addon.INGRESS_ERR_RATE_LIMITED;
51
+ export const ERR_CODE_BODY_TOO_LARGE: number = addon.INGRESS_ERR_BODY_TOO_LARGE;
52
+ export const ERR_CODE_INVALID_JSON: number = addon.INGRESS_ERR_INVALID_JSON;
53
+ export const ERR_CODE_SCHEMA_VALIDATION: number = addon.INGRESS_ERR_SCHEMA_VALIDATION;
54
+ export const ERR_CODE_BAD_REQUEST: number = addon.INGRESS_ERR_BAD_REQUEST;
55
+ export const ERR_CODE_REQUEST_TOO_LARGE: number = addon.INGRESS_ERR_REQUEST_TOO_LARGE;
56
+ export const ERR_CODE_INTERNAL: number = addon.INGRESS_ERR_INTERNAL;