castrum 0.9.0 → 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.
Files changed (120) hide show
  1. package/README.md +153 -630
  2. package/castrum.linux-x64-gnu.node +0 -0
  3. package/dist/index.d.ts +1653 -1817
  4. package/dist/index.js +6172 -4294
  5. package/docs/API.md +337 -0
  6. package/docs/ARCHITECTURE.md +111 -17
  7. package/docs/BENCHMARKS.md +117 -49
  8. package/docs/CASE_STUDY.md +243 -0
  9. package/docs/ENVIRONMENT.md +24 -7
  10. package/docs/FFI_BUN_GUIDE.md +335 -0
  11. package/docs/GETTING_STARTED.md +18 -34
  12. package/docs/HOW_TO_ADD_AN_OP.md +180 -0
  13. package/docs/INGRESS-ROUTER.md +82 -0
  14. package/docs/INGRESS.md +11 -7
  15. package/docs/NATIVE-ROUTE.md +77 -0
  16. package/docs/REPO_MAP.md +103 -53
  17. package/docs/adr/0001-two-wire-formats.md +1 -1
  18. package/docs/adr/0003-bun-builtins-delegation.md +6 -5
  19. package/docs/ai/LOCAL_DEV.md +81 -0
  20. package/docs/ai/TREE.md +1070 -0
  21. package/docs/bun-builtins-decision-matrix.md +25 -18
  22. package/index.ts +9 -16
  23. package/package.json +41 -28
  24. package/src/ingress/body.ts +167 -27
  25. package/src/ingress/decode/result-base.ts +10 -2
  26. package/src/ingress/handlers.ts +116 -285
  27. package/src/ingress/headers/security.ts +41 -0
  28. package/src/ingress/health.ts +1 -0
  29. package/src/ingress/index.ts +45 -169
  30. package/src/ingress/native-route.ts +182 -0
  31. package/src/ingress/packing/gather-raw-headers.ts +110 -22
  32. package/src/ingress/packing/header-packing.ts +5 -5
  33. package/src/ingress/packing/input-packer.ts +17 -3
  34. package/src/ingress/packing/route-wire.ts +197 -0
  35. package/src/ingress/packing/select-headers.ts +20 -7
  36. package/src/ingress/path-matcher.ts +115 -0
  37. package/src/ingress/response/baked-response.ts +302 -0
  38. package/src/ingress/router.ts +277 -0
  39. package/src/ingress/routes/common.ts +50 -1
  40. package/src/ingress/routes/fallback.ts +2 -2
  41. package/src/ingress/routes/head.ts +3 -13
  42. package/src/ingress/routes/index.ts +5 -4
  43. package/src/ingress/routes/json-write.ts +26 -47
  44. package/src/ingress/routes/native.ts +130 -0
  45. package/src/ingress/routes/options.ts +2 -12
  46. package/src/ingress/routes/read.ts +3 -13
  47. package/src/ingress/routes/responder.ts +159 -0
  48. package/src/ingress/server-node.ts +7 -7
  49. package/src/ingress/server.ts +83 -112
  50. package/src/ingress/sync.ts +151 -0
  51. package/src/ingress/types.ts +44 -1
  52. package/src/integration/pipeline.ts +3 -0
  53. package/src/integration/streaming.ts +2 -3
  54. package/src/integration/websocket.ts +4 -3
  55. package/src/loader/batch.ts +4 -0
  56. package/src/loader/cost.ts +1 -0
  57. package/src/loader/index.ts +43 -228
  58. package/src/loader/state.ts +84 -0
  59. package/src/loader/types.ts +145 -0
  60. package/src/native/ffi/build/codecs.ts +736 -0
  61. package/src/native/ffi/build/compress.ts +248 -0
  62. package/src/native/ffi/build/instances.ts +598 -0
  63. package/src/native/ffi/build/parse.ts +539 -0
  64. package/src/native/ffi/build/util.ts +140 -0
  65. package/src/native/ffi/build.ts +94 -0
  66. package/src/native/ffi/constants.ts +58 -0
  67. package/src/native/ffi/selftest.ts +22 -0
  68. package/src/native/ffi/types.ts +499 -0
  69. package/src/native/ffi.ts +307 -1357
  70. package/src/native/loader.ts +52 -6
  71. package/src/native/types/instances.ts +279 -0
  72. package/src/native/types.ts +41 -238
  73. package/src/runtime/builtins.ts +131 -0
  74. package/src/runtime/codec.ts +147 -0
  75. package/src/runtime/detect.ts +36 -0
  76. package/src/runtime/index.ts +28 -0
  77. package/src/runtime/native.ts +35 -0
  78. package/src/runtime/server.ts +31 -0
  79. package/src/runtime/transport.ts +64 -0
  80. package/src/runtime/types.ts +173 -0
  81. package/src/runtime/uuid.ts +45 -0
  82. package/src/runtime/websocket.ts +30 -0
  83. package/src/rust-ffi/addon.ts +1 -1
  84. package/src/rust-ffi/batch/build.ts +155 -0
  85. package/src/rust-ffi/batch/index.ts +8 -0
  86. package/src/rust-ffi/batch/types.ts +92 -0
  87. package/src/rust-ffi/client.ts +5 -6
  88. package/src/rust-ffi/context.ts +39 -10
  89. package/src/rust-ffi/index.ts +28 -17
  90. package/src/rust-ffi/into.ts +21 -0
  91. package/src/rust-ffi/options.ts +1 -1
  92. package/src/rust-ffi/packed.ts +40 -2
  93. package/src/rust-ffi/proven.ts +16 -30
  94. package/src/rust-ffi/scalar/crypto.ts +188 -107
  95. package/src/rust-ffi/scalar/factories.ts +257 -27
  96. package/src/rust-ffi/scalar/hashing.ts +43 -41
  97. package/src/rust-ffi/scalar/http.ts +208 -77
  98. package/src/rust-ffi/scalar/index.ts +3 -3
  99. package/src/rust-ffi/scalar/interface.ts +88 -213
  100. package/src/rust-ffi/scalar/json-packed.ts +175 -0
  101. package/src/rust-ffi/scalar/json.ts +28 -12
  102. package/src/rust-ffi/scalar/payload.ts +159 -42
  103. package/src/rust-ffi/text.ts +31 -5
  104. package/src/selection.ts +27 -31
  105. package/src/shared/adaptive.ts +1 -2
  106. package/src/shared/buffer-pool.ts +72 -17
  107. package/src/shared/bytes.ts +75 -6
  108. package/src/shared/codec.ts +78 -0
  109. package/src/shared/env.ts +4 -4
  110. package/src/shared/log.ts +1 -0
  111. package/src/shared/packed/index.ts +10 -0
  112. package/src/shared/packed/parsers.ts +31 -0
  113. package/src/shared/packed/schema.ts +23 -0
  114. package/src/shared/{packed.ts → packed/wire.ts} +31 -39
  115. package/src/shared/proven.ts +189 -496
  116. package/src/shared/runtime.ts +20 -25
  117. package/src/shared/uuid.ts +12 -28
  118. package/src/data/json-rows.ts +0 -33
  119. package/src/rust-ffi/batch.ts +0 -431
  120. package/src/shared/bench-classify.ts +0 -209
@@ -1,34 +1,29 @@
1
- // src/shared/runtime.ts — runtime detection helpers.
1
+ // src/shared/runtime.ts — runtime detection helpers (facade).
2
2
  //
3
- // castrum is Bun-first but must also run under Node.js. This module is the
4
- // single place that detects the host runtime, so the rest of the codebase can
5
- // branch on `isBun()` / `isNode()` without sprinkling `typeof Bun` checks
6
- // everywhere. Importing this module has zero side effects.
3
+ // Facade over the cached detection in `src/runtime/detect.ts` (the ONLY place
4
+ // `typeof Bun` is checked — resolved ONCE at module load by the runtime
5
+ // adapter). Kept so existing `isBun()` / `isNode()` / `runtimeName()` /
6
+ // `nodeMajorVersion()` call sites work unchanged.
7
7
 
8
- export type RuntimeName = 'bun' | 'node' | 'unknown'
8
+ import {
9
+ detectedRuntime,
10
+ isBunRuntime,
11
+ isNodeRuntime,
12
+ nodeMajorVersion as nodeMajorVersionDetected,
13
+ } from '../runtime/detect'
14
+ import type { RuntimeName as RuntimeNameType } from '../runtime/types'
9
15
 
10
- /** Whether the current runtime is Bun (the primary target). */
11
- export function isBun(): boolean {
12
- // Bun defines the global `Bun` object; Node does not.
13
- return typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined'
14
- }
16
+ /** Host runtime identifier. */
17
+ export type RuntimeName = RuntimeNameType
18
+
19
+ /** Whether the current runtime is Bun (the primary target). Cached once at load. */
20
+ export const isBun: () => boolean = isBunRuntime
15
21
 
16
22
  /** Whether the current runtime is Node.js. */
17
- export function isNode(): boolean {
18
- return !isBun() && typeof process !== 'undefined' && process.versions?.node != null
19
- }
23
+ export const isNode: () => boolean = isNodeRuntime
20
24
 
21
25
  /** Best-effort name of the current runtime. */
22
- export function runtimeName(): RuntimeName {
23
- if (isBun()) return 'bun'
24
- if (isNode()) return 'node'
25
- return 'unknown'
26
- }
26
+ export const runtimeName: () => RuntimeName = () => detectedRuntime
27
27
 
28
28
  /** The Node.js major version when running under Node, otherwise `null`. */
29
- export function nodeMajorVersion(): number | null {
30
- if (!isNode()) return null
31
- const v = process.versions.node
32
- const major = Number(v?.split('.')[0])
33
- return Number.isFinite(major) ? major : null
34
- }
29
+ export const nodeMajorVersion: () => number | null = nodeMajorVersionDetected
@@ -1,31 +1,15 @@
1
- // src/shared/uuid.ts — UUIDv7 generation.
1
+ // src/shared/uuid.ts — UUIDv7 generation (facade).
2
2
  //
3
- // Deliberately does NOT implement UUIDv7 in Rust: the decision matrix
4
- // (docs/bun-builtins-decision-matrix.md) measured Bun.randomUUIDv7 ~2x faster
5
- // than the FFI-crossing rust random-token path, so this delegates to the Bun
6
- // built-in and falls back to crypto.randomUUID (RFC 4122 v4) under Node.
3
+ // UUIDv7 delegates to the runtime adapter's uuid (`src/runtime/uuid.ts`):
4
+ // `Bun.randomUUIDv7` on Bun (measured ~2x faster than the FFI-crossing rust
5
+ // path) and `crypto.randomUUID` (RFC 4122 v4) under Node.
7
6
 
8
- /** Generate a UUIDv7 (time-ordered) string, preferring Bun's native built-in. */
9
- export function uuidv7(): string {
10
- const bun = (globalThis as { Bun?: { randomUUIDv7?: () => string } }).Bun
11
- if (typeof bun?.randomUUIDv7 === 'function') {
12
- return bun.randomUUIDv7()
13
- }
14
- // Node / non-Bun fallback: crypto.randomUUID (v4 — order not guaranteed).
15
- return globalThis.crypto?.randomUUID?.() ?? fallbackRandomUuid()
16
- }
7
+ import { runtimeUuid } from '../runtime/uuid'
17
8
 
18
- /** Last-resort synchronous UUIDv4 (only if crypto.randomUUID is unavailable). */
19
- function fallbackRandomUuid(): string {
20
- const bytes = new Uint8Array(16)
21
- const c = globalThis.crypto
22
- if (c?.getRandomValues) {
23
- c.getRandomValues(bytes)
24
- } else {
25
- for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256)
26
- }
27
- bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40 // version 4
28
- bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80 // variant 10xx
29
- const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')
30
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
31
- }
9
+ /**
10
+ * Generate a UUIDv7 (time-ordered) string, preferring Bun's native built-in.
11
+ * Under Node this falls back to `crypto.randomUUID` (v4).
12
+ *
13
+ * @returns A UUID string.
14
+ */
15
+ export const uuidv7: () => string = runtimeUuid.uuidv7
@@ -1,33 +0,0 @@
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
- }
@@ -1,431 +0,0 @@
1
- // src/rust-ffi/batch.ts — Array-of-bytes FFI namespace.
2
- //
3
- // `rust.batch.*` packs an array of byte slices into the packed wire format,
4
- // calls the native batch function, and unpacks the result into the friendly
5
- // JS shape (bitsets, u32 arrays, i64 arrays, byte results).
6
-
7
- import {
8
- schemaValidateBatch,
9
- schemaValidateBatchCount,
10
- unpackBitset,
11
- unpackByteResults,
12
- unpackI64ArrayAsBigInt,
13
- unpackMultipartParts,
14
- unpackU32Array,
15
- unpackU64ArrayAsBigInt,
16
- withPackScratch,
17
- withPackScratch2,
18
- type SchemaValidator,
19
- } from '../shared/packed'
20
- import { resolvePoolNative, type RustClientContext } from './context'
21
- import type { MultipartPart, PasswordHashOptions } from '../native'
22
-
23
- /** Array-of-bytes FFI namespace. */
24
- export interface RustBatch {
25
- jsonValid(items: Uint8Array[]): Uint8Array
26
- crc32(items: Uint8Array[]): Uint32Array
27
- validateEmail(items: Uint8Array[]): Uint8Array
28
- validateUuid(items: Uint8Array[]): Uint8Array
29
- validateIpv4(items: Uint8Array[]): Uint8Array
30
- validateIpv6(items: Uint8Array[]): Uint8Array
31
- jsonSumIds(items: Uint8Array[]): BigInt64Array
32
- queryParse(items: Uint8Array[]): Uint8Array[]
33
- cookieParse(items: Uint8Array[]): Uint8Array[]
34
- httpParseRequest(items: Uint8Array[]): Uint8Array[]
35
- /** Apply N RFC 6902 patches (docs and patches zipped). Fail-fast on error. */
36
- jsonPatch(docs: Uint8Array[], patches: Uint8Array[]): Uint8Array[]
37
- /** Encode N items to lowercase hex (packed in → byte results). */
38
- hexEncode(items: Uint8Array[]): Uint8Array[]
39
- /** Decode N lowercase/uppercase hex strings to bytes. */
40
- hexDecode(items: Uint8Array[]): Uint8Array[]
41
- /** Base64-encode N items (standard by default; url-safe/padding configurable). */
42
- base64Encode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[]
43
- /** Base64-decode N items (standard by default; url-safe/padding configurable). */
44
- base64Decode(items: Uint8Array[], urlSafe?: boolean, padding?: boolean): Uint8Array[]
45
- formParse(items: Uint8Array[]): Uint8Array[]
46
- /** Sign N cookie values (packed → byte results). */
47
- signCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array[]
48
- /** Verify N signed cookies (bitset of valid). */
49
- verifyCookie(items: Uint8Array[], secret: Uint8Array): Uint8Array
50
- /** Verify N CSRF tokens (bitset of valid). */
51
- csrfVerify(items: Uint8Array[], secret: Uint8Array): Uint8Array
52
- schemaValidate(validator: SchemaValidator, items: Uint8Array[]): Uint8Array
53
- schemaValidateCount(validator: SchemaValidator, items: Uint8Array[]): number
54
- /** Hash N items with FNV-1a 64 (packed → unsigned u64 array). */
55
- fnv1a64(items: Uint8Array[]): BigUint64Array
56
- /** Generate N ETags (strong by default; weak flag applies to all items). */
57
- etag(items: Uint8Array[], weak?: boolean | null): Uint8Array[]
58
- urlEncode(items: Uint8Array[]): Uint8Array[]
59
- /** Percent-decode N strings (UTF-8 validated; malformed items → empty). */
60
- urlDecode(items: Uint8Array[]): Uint8Array[]
61
- /** Strict percent-decode N strings (no UTF-8 validation). */
62
- urlDecodeBytes(items: Uint8Array[]): Uint8Array[]
63
- mimeFromExtension(items: Uint8Array[]): Uint8Array[]
64
- wsAcceptKey(items: Uint8Array[]): Uint8Array[]
65
- /** Base64url-encode N items (URL-safe, no padding). */
66
- base64UrlEncode(items: Uint8Array[]): Uint8Array[]
67
- /** Base64url-decode N items (URL-safe, no padding). */
68
- base64UrlDecode(items: Uint8Array[]): Uint8Array[]
69
-
70
- // ── Backend-framework features ──
71
- passwordHash(
72
- passwords: Uint8Array[],
73
- salt: Uint8Array,
74
- options?: PasswordHashOptions | null,
75
- ): Uint8Array[]
76
- aeadEncrypt(
77
- key: Uint8Array,
78
- nonce: Uint8Array,
79
- items: Uint8Array[],
80
- algorithm?: string | null,
81
- ): Uint8Array[]
82
- aeadDecrypt(
83
- key: Uint8Array,
84
- nonce: Uint8Array,
85
- items: Uint8Array[],
86
- algorithm?: string | null,
87
- ): Uint8Array[]
88
- gzipCompress(items: Uint8Array[], level?: number | null): Uint8Array[]
89
- gzipDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[]
90
- brotliCompress(items: Uint8Array[], quality?: number | null): Uint8Array[]
91
- brotliDecompress(items: Uint8Array[], maxDecompressed?: number | null): Uint8Array[]
92
- jwtVerify(tokens: Uint8Array[], secret: Uint8Array, nowSeconds: number): Uint8Array
93
- /** Sign N messages to lowercase-hex HMAC-SHA256 signatures (packed → byte results). */
94
- hmacSha256(items: Uint8Array[], key: Uint8Array): Uint8Array[]
95
- /** Verify N message + hex-signature pairs (two packed lists, zipped) → bitset of valid. */
96
- hmacSha256Verify(items: Uint8Array[], sigs: Uint8Array[], key: Uint8Array): Uint8Array
97
- /** Verify N password+PHC pairs (two packed lists, zipped) → bitset of valid. */
98
- passwordVerify(items: Uint8Array[], phcs: Uint8Array[]): Uint8Array
99
- /** Resolve N references against N bases (two packed lists, zipped). */
100
- urlResolve(bases: Uint8Array[], references: Uint8Array[]): Uint8Array[]
101
- /** Sign N JSON claim documents (packed → signed tokens). */
102
- jwtSign(
103
- items: Uint8Array[],
104
- secret: Uint8Array,
105
- ttlSeconds?: number | null,
106
- nowSeconds?: number,
107
- ): Uint8Array[]
108
- /** Encode N SSE events with shared event/id/retry. */
109
- sseEncode(
110
- items: Uint8Array[],
111
- event?: string | null,
112
- id?: string | null,
113
- retry?: number | null,
114
- ): Uint8Array[]
115
- /** Encode N frames (same opcode/mask/fin) from payloads. */
116
- wsFrameEncode(items: Uint8Array[], opcode: number, mask: boolean, fin: boolean): Uint8Array[]
117
- /** Decode N frames → payloads (opcodes/flags dropped; failed items → empty). */
118
- wsFrameDecode(items: Uint8Array[]): Uint8Array[]
119
- /** Parse N multipart bodies → array of parts per item (same boundary). */
120
- multipartParse(items: Uint8Array[], boundary: Uint8Array): MultipartPart[][]
121
- /**
122
- * @performance Template render: ~1.7x slower than the JS baseline (1/3 tasks failed) [check:annotate]
123
- * @deprecated Slower than the native JS baseline (~1.7x) — prefer the JS/Bun baseline. [check:annotate]
124
- * @remarks Promotion candidate: 2/3 benchmarks won in the latest run (static: not-competitive). [check:annotate]
125
- * @remarks The allocating render marshals the context via napi DOM (~1.5x loss). Use TemplateRenderer.renderBytes (JSON-bytes context) which avoids it and wins ~1.37x. [check:annotate]
126
- */
127
- templateRender(source: string, contexts: Uint8Array[]): Uint8Array[]
128
- }
129
-
130
- // Native batch fns are process-wide singletons (one lazy addon). `resolvePoolNative`
131
- // resolves each once (triggering pool init + addon load on first use) and caches
132
- // it, so the hot path skips the lazy-addon Proxy get on every call — the same
133
- // cache `resolveNative`/packed use (context.ts), with pool init for the
134
- // rayon-backed batch surface.
135
- function nativeFn(ctx: RustClientContext, name: string): (...args: unknown[]) => unknown {
136
- return resolvePoolNative(ctx, name)
137
- }
138
-
139
- /** Build the `batch` namespace for a client context. */
140
- export function buildBatch(ctx: RustClientContext): RustBatch {
141
- return {
142
- jsonValid(items) {
143
- return withPackScratch(items, (packed) =>
144
- unpackBitset(nativeFn(ctx, 'jsonValidBatchPacked')(packed) as Buffer),
145
- )
146
- },
147
- crc32(items) {
148
- return withPackScratch(items, (packed) =>
149
- unpackU32Array(nativeFn(ctx, 'crc32BatchPacked')(packed) as Buffer),
150
- )
151
- },
152
- validateEmail(items) {
153
- return withPackScratch(items, (packed) =>
154
- unpackBitset(nativeFn(ctx, 'validateEmailBatchPacked')(packed) as Buffer),
155
- )
156
- },
157
- validateUuid(items) {
158
- return withPackScratch(items, (packed) =>
159
- unpackBitset(nativeFn(ctx, 'validateUuidBatchPacked')(packed) as Buffer),
160
- )
161
- },
162
- validateIpv4(items) {
163
- return withPackScratch(items, (packed) =>
164
- unpackBitset(nativeFn(ctx, 'validateIpv4BatchPacked')(packed) as Buffer),
165
- )
166
- },
167
- validateIpv6(items) {
168
- return withPackScratch(items, (packed) =>
169
- unpackBitset(nativeFn(ctx, 'validateIpv6BatchPacked')(packed) as Buffer),
170
- )
171
- },
172
- jsonSumIds(items) {
173
- return withPackScratch(items, (packed) =>
174
- unpackI64ArrayAsBigInt(nativeFn(ctx, 'jsonSumBatchPacked')(packed) as Buffer),
175
- )
176
- },
177
- queryParse(items) {
178
- return withPackScratch(items, (packed) =>
179
- unpackByteResults(nativeFn(ctx, 'queryParseBatchPacked')(packed) as Buffer),
180
- )
181
- },
182
- cookieParse(items) {
183
- return withPackScratch(items, (packed) =>
184
- unpackByteResults(nativeFn(ctx, 'cookieParseBatchPacked')(packed) as Buffer),
185
- )
186
- },
187
- formParse(items) {
188
- return withPackScratch(items, (packed) =>
189
- unpackByteResults(nativeFn(ctx, 'formParseBatchPacked')(packed) as Buffer),
190
- )
191
- },
192
- signCookie(items, secret) {
193
- return withPackScratch(items, (packed) =>
194
- unpackByteResults(nativeFn(ctx, 'signCookieBatchPacked')(packed, secret) as Buffer),
195
- )
196
- },
197
- verifyCookie(items, secret) {
198
- return withPackScratch(items, (packed) =>
199
- unpackBitset(nativeFn(ctx, 'verifyCookieBatchPacked')(packed, secret) as Buffer),
200
- )
201
- },
202
- csrfVerify(items, secret) {
203
- return withPackScratch(items, (packed) =>
204
- unpackBitset(nativeFn(ctx, 'csrfVerifyBatchPacked')(packed, secret) as Buffer),
205
- )
206
- },
207
- httpParseRequest(items) {
208
- return withPackScratch(items, (packed) =>
209
- unpackByteResults(nativeFn(ctx, 'httpParseRequestBatchPacked')(packed) as Buffer),
210
- )
211
- },
212
- fnv1a64(items) {
213
- return withPackScratch(items, (packed) =>
214
- unpackU64ArrayAsBigInt(nativeFn(ctx, 'fnv1A64BatchPacked')(packed) as Buffer),
215
- )
216
- },
217
- etag(items, weak) {
218
- return withPackScratch(items, (packed) =>
219
- unpackByteResults(nativeFn(ctx, 'etagBatchPacked')(packed, weak ?? null) as Buffer),
220
- )
221
- },
222
- urlEncode(items) {
223
- return withPackScratch(items, (packed) =>
224
- unpackByteResults(nativeFn(ctx, 'urlEncodeBatchPacked')(packed) as Buffer),
225
- )
226
- },
227
- urlDecode(items) {
228
- return withPackScratch(items, (packed) =>
229
- unpackByteResults(nativeFn(ctx, 'urlDecodeBatchPacked')(packed) as Buffer),
230
- )
231
- },
232
- urlDecodeBytes(items) {
233
- return withPackScratch(items, (packed) =>
234
- unpackByteResults(nativeFn(ctx, 'urlDecodeBytesBatchPacked')(packed) as Buffer),
235
- )
236
- },
237
- mimeFromExtension(items) {
238
- return withPackScratch(items, (packed) =>
239
- unpackByteResults(nativeFn(ctx, 'mimeFromExtensionBatchPacked')(packed) as Buffer),
240
- )
241
- },
242
- wsAcceptKey(items) {
243
- return withPackScratch(items, (packed) =>
244
- unpackByteResults(nativeFn(ctx, 'wsAcceptKeyBatchPacked')(packed) as Buffer),
245
- )
246
- },
247
- base64UrlEncode(items) {
248
- return withPackScratch(items, (packed) =>
249
- unpackByteResults(nativeFn(ctx, 'base64EncodeBatchPacked')(packed, true, false) as Buffer),
250
- )
251
- },
252
- base64UrlDecode(items) {
253
- return withPackScratch(items, (packed) =>
254
- unpackByteResults(nativeFn(ctx, 'base64DecodeBatchPacked')(packed, true, false) as Buffer),
255
- )
256
- },
257
- jsonPatch(docs, patches) {
258
- return withPackScratch2(docs, patches, (packedDocs, packedPatches) =>
259
- unpackByteResults(
260
- nativeFn(ctx, 'jsonPatchBatchPacked')(packedDocs, packedPatches) as Buffer,
261
- ),
262
- )
263
- },
264
- hexEncode(items) {
265
- return withPackScratch(items, (packed) =>
266
- unpackByteResults(nativeFn(ctx, 'hexEncodeBatchPacked')(packed) as Buffer),
267
- )
268
- },
269
- hexDecode(items) {
270
- return withPackScratch(items, (packed) =>
271
- unpackByteResults(nativeFn(ctx, 'hexDecodeBatchPacked')(packed) as Buffer),
272
- )
273
- },
274
- base64Encode(items, urlSafe, padding) {
275
- return withPackScratch(items, (packed) =>
276
- unpackByteResults(
277
- nativeFn(ctx, 'base64EncodeBatchPacked')(
278
- packed,
279
- urlSafe ?? null,
280
- padding ?? null,
281
- ) as Buffer,
282
- ),
283
- )
284
- },
285
- base64Decode(items, urlSafe, padding) {
286
- return withPackScratch(items, (packed) =>
287
- unpackByteResults(
288
- nativeFn(ctx, 'base64DecodeBatchPacked')(
289
- packed,
290
- urlSafe ?? null,
291
- padding ?? null,
292
- ) as Buffer,
293
- ),
294
- )
295
- },
296
- schemaValidate(validator, items) {
297
- return schemaValidateBatch(validator, items)
298
- },
299
- schemaValidateCount(validator, items) {
300
- return schemaValidateBatchCount(validator, items)
301
- },
302
-
303
- // ── Backend-framework features ──
304
- passwordHash(password, salt, options) {
305
- return withPackScratch(password, (packed) =>
306
- unpackByteResults(
307
- nativeFn(ctx, 'passwordHashBatchPacked')(packed, salt, options ?? null) as Buffer,
308
- ),
309
- )
310
- },
311
- aeadEncrypt(key, nonce, items, algorithm) {
312
- return withPackScratch(items, (packed) =>
313
- unpackByteResults(
314
- nativeFn(ctx, 'aeadEncryptBatchPacked')(packed, key, nonce, algorithm ?? null) as Buffer,
315
- ),
316
- )
317
- },
318
- aeadDecrypt(key, nonce, items, algorithm) {
319
- return withPackScratch(items, (packed) =>
320
- unpackByteResults(
321
- nativeFn(ctx, 'aeadDecryptBatchPacked')(packed, key, nonce, algorithm ?? null) as Buffer,
322
- ),
323
- )
324
- },
325
- gzipCompress(items, level) {
326
- return withPackScratch(items, (packed) =>
327
- unpackByteResults(
328
- nativeFn(ctx, 'gzipCompressBatchPacked')(packed, level ?? null) as Buffer,
329
- ),
330
- )
331
- },
332
- gzipDecompress(items, maxDecompressed) {
333
- return withPackScratch(items, (packed) =>
334
- unpackByteResults(
335
- nativeFn(ctx, 'gzipDecompressBatchPacked')(packed, maxDecompressed ?? null) as Buffer,
336
- ),
337
- )
338
- },
339
- brotliCompress(items, quality) {
340
- return withPackScratch(items, (packed) =>
341
- unpackByteResults(
342
- nativeFn(ctx, 'brotliCompressBatchPacked')(packed, quality ?? null) as Buffer,
343
- ),
344
- )
345
- },
346
- brotliDecompress(items, maxDecompressed) {
347
- return withPackScratch(items, (packed) =>
348
- unpackByteResults(
349
- nativeFn(ctx, 'brotliDecompressBatchPacked')(packed, maxDecompressed ?? null) as Buffer,
350
- ),
351
- )
352
- },
353
- jwtVerify(tokens, secret, nowSeconds) {
354
- return withPackScratch(tokens, (packed) =>
355
- unpackBitset(nativeFn(ctx, 'jwtVerifyBatchPacked')(packed, secret, nowSeconds) as Buffer),
356
- )
357
- },
358
- hmacSha256(items, key) {
359
- return withPackScratch(items, (packed) =>
360
- unpackByteResults(nativeFn(ctx, 'hmacSha256BatchPacked')(packed, key) as Buffer),
361
- )
362
- },
363
- hmacSha256Verify(items, sigs, key) {
364
- return withPackScratch2(items, sigs, (packedItems, packedSigs) =>
365
- unpackBitset(
366
- nativeFn(ctx, 'hmacSha256VerifyBatchPacked')(packedItems, packedSigs, key) as Buffer,
367
- ),
368
- )
369
- },
370
- templateRender(source, contexts) {
371
- return withPackScratch(contexts, (packed) =>
372
- unpackByteResults(nativeFn(ctx, 'templateRenderBatchPacked')(packed, source) as Buffer),
373
- )
374
- },
375
- passwordVerify(items, phcs) {
376
- return withPackScratch2(items, phcs, (packedItems, packedPhcs) =>
377
- unpackBitset(nativeFn(ctx, 'passwordVerifyBatchPacked')(packedItems, packedPhcs) as Buffer),
378
- )
379
- },
380
- urlResolve(bases, references) {
381
- return withPackScratch2(bases, references, (packedBases, packedRefs) =>
382
- unpackByteResults(
383
- nativeFn(ctx, 'urlResolveBatchPacked')(packedBases, packedRefs) as Buffer,
384
- ),
385
- )
386
- },
387
- jwtSign(items, secret, ttlSeconds, nowSeconds) {
388
- return withPackScratch(items, (packed) =>
389
- unpackByteResults(
390
- nativeFn(ctx, 'jwtSignBatchPacked')(
391
- packed,
392
- secret,
393
- ttlSeconds ?? null,
394
- nowSeconds ?? Math.floor(Date.now() / 1000),
395
- ) as Buffer,
396
- ),
397
- )
398
- },
399
- sseEncode(items, event, id, retry) {
400
- return withPackScratch(items, (packed) =>
401
- unpackByteResults(
402
- nativeFn(ctx, 'sseEncodeBatchPacked')(
403
- packed,
404
- event ?? null,
405
- id ?? null,
406
- retry ?? null,
407
- ) as Buffer,
408
- ),
409
- )
410
- },
411
- wsFrameEncode(items, opcode, mask, fin) {
412
- return withPackScratch(items, (packed) =>
413
- unpackByteResults(
414
- nativeFn(ctx, 'wsFrameEncodeBatchPacked')(packed, opcode, mask, fin) as Buffer,
415
- ),
416
- )
417
- },
418
- wsFrameDecode(items) {
419
- return withPackScratch(items, (packed) =>
420
- unpackByteResults(nativeFn(ctx, 'wsFrameDecodeBatchPacked')(packed) as Buffer),
421
- )
422
- },
423
- multipartParse(items, boundary) {
424
- return withPackScratch(items, (packed) =>
425
- unpackMultipartParts(
426
- nativeFn(ctx, 'multipartParseBatchPacked')(packed, boundary) as Buffer,
427
- ),
428
- )
429
- },
430
- }
431
- }