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,207 @@
1
+ import { existsSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { join, dirname } from "node:path";
4
+ import { createRequire } from "node:module";
5
+
6
+ const require = createRequire(import.meta.url);
7
+
8
+ export interface IngressInstance {
9
+ handleRequestPacked(
10
+ input: Uint8Array,
11
+ body: Uint8Array | null,
12
+ output: Uint8Array,
13
+ ): number;
14
+
15
+ handleRequestFullSync(
16
+ methodKind: number,
17
+ url: string,
18
+ ip: string,
19
+ requestId: string,
20
+ headers: Array<[string, string]>,
21
+ body: Uint8Array | null,
22
+ outputBufferSize?: number,
23
+ ): Uint8Array;
24
+ }
25
+
26
+ function resolveAddonPath(): string {
27
+ const __dirname = dirname(fileURLToPath(import.meta.url));
28
+ const platform = process.platform;
29
+ const arch = process.arch;
30
+ const base = join(__dirname, "..", "..");
31
+
32
+ // napi-rs artifact naming: castrum.<platform>-<arch>[-<libc>].node
33
+ // e.g. linux-x64-gnu, linux-x64-musl, win32-x64-msvc, darwin-arm64.
34
+ const libcVariants =
35
+ platform === "win32"
36
+ ? ["msvc", "gnu"]
37
+ : platform === "linux"
38
+ ? ["gnu", "musl"]
39
+ : [""];
40
+
41
+ const names = libcVariants.map(
42
+ (libc) => `castrum.${platform}-${arch}${libc ? `-${libc}` : ""}.node`,
43
+ );
44
+ names.push("castrum.node");
45
+
46
+ const roots = [base, join(base, "target", "release")];
47
+
48
+ const candidates: string[] = [];
49
+ for (const root of roots) {
50
+ for (const name of names) {
51
+ candidates.push(join(root, name));
52
+ }
53
+ }
54
+
55
+ for (const candidate of candidates) {
56
+ if (existsSync(candidate)) {
57
+ return candidate;
58
+ }
59
+ }
60
+
61
+ throw new Error(
62
+ `Could not find castrum native addon.\n` +
63
+ `Run: bun run build\n` +
64
+ `Looked in:\n${candidates.map((c) => ` - ${c}`).join("\n")}`,
65
+ );
66
+ }
67
+
68
+ interface NativeAddon {
69
+ HmacSigner: new (key: Uint8Array) => HmacSignerInstance;
70
+ Ingress: new (options: Record<string, unknown>) => IngressInstance;
71
+
72
+ // ── Ingress binary-layout constants (single source of truth: rust/ingress_constants.rs) ──
73
+ INGRESS_OUT_VERDICT: number;
74
+ INGRESS_OUT_ERROR_CODE: number;
75
+ INGRESS_OUT_STATUS: number;
76
+ INGRESS_OUT_FLAGS: number;
77
+ INGRESS_OUT_RATE_LIMIT: number;
78
+ INGRESS_OUT_RATE_REMAINING: number;
79
+ INGRESS_OUT_RATE_RESET: number;
80
+ INGRESS_OUT_RETRY_AFTER: number;
81
+ INGRESS_OUT_COOKIES_JSON_LEN: number;
82
+ INGRESS_OUT_QUERY_JSON_LEN: number;
83
+ INGRESS_OUT_HEADER_VARIANT: number;
84
+ INGRESS_OUT_BODY_JSON_LEN: number;
85
+ INGRESS_OUT_DATA_START: number;
86
+ INGRESS_FLAG_HAS_COOKIES: number;
87
+ INGRESS_FLAG_HAS_QUERY: number;
88
+ INGRESS_FLAG_BODY_VALID_JSON: number;
89
+ INGRESS_FLAG_SCHEMA_VALID: number;
90
+ INGRESS_FLAG_CORS_ALLOWED: number;
91
+ INGRESS_FLAG_IS_PREFLIGHT: number;
92
+ INGRESS_FLAG_RATE_LIMITED: number;
93
+ INGRESS_FLAG_HTTPS: number;
94
+ INGRESS_FLAG_TRUSTED_PROXY: number;
95
+ INGRESS_FLAG_BODY_TRUNCATED: number;
96
+ INGRESS_HV_JSON: number;
97
+ INGRESS_HV_CORS_SIMPLE: number;
98
+ INGRESS_HV_CORS_PREFLIGHT: number;
99
+ INGRESS_HV_RATE_ACTIVE: number;
100
+ INGRESS_HV_RATE_LIMITED: number;
101
+ INGRESS_HV_COUNT: number;
102
+ INGRESS_ERR_NONE: number;
103
+ INGRESS_ERR_CORS_PREFLIGHT: number;
104
+ INGRESS_ERR_RATE_LIMITED: number;
105
+ INGRESS_ERR_BODY_TOO_LARGE: number;
106
+ INGRESS_ERR_INVALID_JSON: number;
107
+ INGRESS_ERR_SCHEMA_VALIDATION: number;
108
+ INGRESS_ERR_BAD_REQUEST: number;
109
+ INGRESS_ERR_REQUEST_TOO_LARGE: number;
110
+ INGRESS_ERR_INTERNAL: number;
111
+
112
+ crc32(input: Uint8Array): number;
113
+ fnv1a64(input: Uint8Array): bigint;
114
+
115
+ hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
116
+ hmacSha256Verify(key: Uint8Array, data: Uint8Array, sig: Uint8Array): boolean;
117
+
118
+ jsonValid(input: Uint8Array): boolean;
119
+ jsonSumIds(input: Uint8Array): bigint;
120
+
121
+ jsonPatch(doc: Uint8Array, patch: Uint8Array): Uint8Array;
122
+
123
+ mimeFromExtension(ext: Uint8Array): Uint8Array;
124
+ randomToken(byteLen: number): Uint8Array;
125
+
126
+ urlEncode(input: Uint8Array): Uint8Array;
127
+ urlDecode(input: Uint8Array): Uint8Array;
128
+ urlDecodeBytes(input: Uint8Array): Uint8Array;
129
+
130
+ validateEmail(input: Uint8Array): boolean;
131
+ validateUuid(input: Uint8Array): boolean;
132
+ validateIpv4(input: Uint8Array): boolean;
133
+ validateIpv6(input: Uint8Array): boolean;
134
+
135
+ wsAcceptKey(key: Uint8Array): Uint8Array;
136
+
137
+ SchemaValidator: new (schema: Uint8Array) => SchemaValidatorInstance;
138
+
139
+ initThreadPool(rayonThreads?: number): void;
140
+ rayonNumThreads(): number;
141
+
142
+ jsonValidBatchPacked(input: Uint8Array): Uint8Array;
143
+ validateEmailBatchPacked(input: Uint8Array): Uint8Array;
144
+ validateUuidBatchPacked(input: Uint8Array): Uint8Array;
145
+ validateIpv4BatchPacked(input: Uint8Array): Uint8Array;
146
+ validateIpv6BatchPacked(input: Uint8Array): Uint8Array;
147
+
148
+ jsonSumBatchPacked(input: Uint8Array): Uint8Array;
149
+ queryParseBatchPacked(input: Uint8Array): Uint8Array;
150
+ cookieParseBatchPacked(input: Uint8Array): Uint8Array;
151
+ httpParseRequestBatchPacked(input: Uint8Array): Uint8Array;
152
+
153
+ httpParseRequestPacked(input: Uint8Array): Uint8Array;
154
+
155
+ queryParsePacked(input: Uint8Array): Uint8Array;
156
+
157
+ cookieParsePacked(input: Uint8Array): Uint8Array;
158
+
159
+ crc32BatchPacked(input: Uint8Array): Uint8Array;
160
+
161
+ // ── Batch metadata / counts ──
162
+ jsonValidBatchCountPacked(input: Uint8Array): number;
163
+ validateEmailBatchCountPacked(input: Uint8Array): number;
164
+ validateUuidBatchCountPacked(input: Uint8Array): number;
165
+ validateIpv4BatchCountPacked(input: Uint8Array): number;
166
+ validateIpv6BatchCountPacked(input: Uint8Array): number;
167
+ jsonSumBatchTotalPacked(input: Uint8Array): number;
168
+ queryParseBatchTotalLenPacked(input: Uint8Array): number;
169
+ cookieParseBatchTotalLenPacked(input: Uint8Array): number;
170
+ httpParseRequestBatchTotalLenPacked(input: Uint8Array): number;
171
+ }
172
+
173
+ const addonPath = resolveAddonPath();
174
+
175
+ let addon: NativeAddon;
176
+ try {
177
+ addon = require(addonPath);
178
+ } catch (err) {
179
+ const cause = err instanceof Error ? `\n Underlying cause: ${err.message}` : "";
180
+ throw new Error(
181
+ `Failed to load castrum native addon from:\n ${addonPath}\n` +
182
+ `The addon exists but could not be loaded (ABI mismatch, missing system ` +
183
+ `libraries, or a corrupt/partial artifact).\n` +
184
+ `Run: bun run build\n` +
185
+ `If the problem persists, verify the binary was built for this platform/CPU.` +
186
+ cause,
187
+ );
188
+ }
189
+
190
+ if (process.env.CASTRUM_DEBUG || process.env.RUST_BENCH_DEBUG) {
191
+ console.log("Native addon loaded from:", addonPath);
192
+ console.log("Exported keys:", Object.keys(addon).sort());
193
+ }
194
+
195
+ export interface SchemaValidatorInstance {
196
+ validateBatchPackedCount(packed: Uint8Array): number;
197
+ validateBatchPackedBitset(packed: Uint8Array): Uint8Array;
198
+ validateBatchStreaming(batchBytes: Uint8Array): number;
199
+ }
200
+
201
+ export interface HmacSignerInstance {
202
+ sign(data: Uint8Array): Uint8Array;
203
+ verify(data: Uint8Array, sig: Uint8Array): boolean;
204
+ }
205
+
206
+ export default addon;
207
+ export { addonPath };