castrum 0.7.0 → 0.9.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.
- package/LICENSE +21 -0
- package/README.md +322 -59
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +3161 -0
- package/dist/index.js +7306 -0
- package/docs/ARCHITECTURE.md +415 -0
- package/docs/BENCHMARKS.md +307 -0
- package/docs/ENVIRONMENT.md +79 -0
- package/docs/GETTING_STARTED.md +209 -0
- package/docs/INGRESS.md +745 -0
- package/docs/REPO_MAP.md +259 -0
- package/docs/adr/0001-two-wire-formats.md +39 -0
- package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
- package/docs/adr/0003-bun-builtins-delegation.md +42 -0
- package/docs/bun-builtins-decision-matrix.md +108 -0
- package/index.ts +51 -8
- package/package.json +73 -20
- package/src/data/json-rows.ts +13 -13
- package/src/ingress/body.ts +149 -0
- package/src/ingress/constants.ts +220 -48
- package/src/ingress/context.ts +136 -0
- package/src/ingress/decode/baked-result.ts +121 -0
- package/src/ingress/decode/fast-result.ts +160 -0
- package/src/ingress/decode/packed-sections.ts +56 -0
- package/src/ingress/decode/result-base.ts +172 -0
- package/src/ingress/errors.ts +72 -0
- package/src/ingress/fast.ts +152 -1021
- package/src/ingress/handlers.ts +563 -1001
- package/src/ingress/headers/baked-templates.ts +102 -0
- package/src/ingress/headers/cors.ts +44 -0
- package/src/ingress/headers/fast-templates.ts +238 -0
- package/src/ingress/headers/hsts.ts +83 -0
- package/src/ingress/health.ts +67 -0
- package/src/ingress/index.ts +130 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +75 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +150 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +96 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/routes/common.ts +38 -0
- package/src/ingress/routes/delete.ts +14 -0
- package/src/ingress/routes/echo.ts +107 -0
- package/src/ingress/routes/fallback.ts +23 -0
- package/src/ingress/routes/head.ts +37 -0
- package/src/ingress/routes/index.ts +10 -0
- package/src/ingress/routes/json-write.ts +153 -0
- package/src/ingress/routes/options.ts +36 -0
- package/src/ingress/routes/read.ts +45 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +443 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +157 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +217 -0
- package/src/integration/streaming.ts +79 -0
- package/src/integration/websocket.ts +92 -0
- package/src/loader/batch.ts +133 -0
- package/src/loader/cost.ts +104 -0
- package/src/loader/index.ts +930 -0
- package/src/loader/ops.ts +657 -0
- package/src/native/ffi.ts +1541 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +194 -0
- package/src/native/types.ts +598 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch.ts +431 -0
- package/src/rust-ffi/client.ts +88 -0
- package/src/rust-ffi/context.ts +164 -0
- package/src/rust-ffi/index.ts +40 -541
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +42 -0
- package/src/rust-ffi/scalar/crypto.ts +265 -0
- package/src/rust-ffi/scalar/factories.ts +233 -0
- package/src/rust-ffi/scalar/hashing.ts +72 -0
- package/src/rust-ffi/scalar/http.ts +206 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +450 -0
- package/src/rust-ffi/scalar/json.ts +45 -0
- package/src/rust-ffi/scalar/payload.ts +124 -0
- package/src/rust-ffi/text.ts +95 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +77 -0
- package/src/shared/adaptive.ts +68 -0
- package/src/shared/bench-classify.ts +209 -0
- package/src/shared/buffer-pool.ts +209 -0
- package/src/shared/bytes.ts +35 -5
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +74 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed.ts +350 -154
- package/src/shared/proven.ts +596 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +34 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +31 -0
- package/Cargo.toml +0 -73
- package/bench.ts +0 -3
- package/rust/batch.rs +0 -195
- package/rust/cookie_parser.rs +0 -70
- package/rust/cors.rs +0 -263
- package/rust/hashing.rs +0 -164
- package/rust/headers.rs +0 -234
- package/rust/hmac_sha256.rs +0 -113
- package/rust/http_parser.rs +0 -132
- package/rust/ingress.rs +0 -616
- package/rust/ingress_constants.rs +0 -89
- package/rust/ip_trust.rs +0 -185
- package/rust/json_ops.rs +0 -66
- package/rust/json_patch_ops.rs +0 -25
- package/rust/json_schema.rs +0 -151
- package/rust/json_ser.rs +0 -429
- package/rust/lib.rs +0 -48
- package/rust/method.rs +0 -141
- package/rust/mime_lookup.rs +0 -76
- package/rust/output.rs +0 -238
- package/rust/proxy.rs +0 -90
- package/rust/query_parser.rs +0 -141
- package/rust/random_token.rs +0 -52
- package/rust/rate_limit.rs +0 -203
- package/rust/terminal.rs +0 -95
- package/rust/unit_tests.rs +0 -843
- package/rust/url_codec.rs +0 -133
- package/rust/util.rs +0 -537
- package/rust/validation.rs +0 -107
- package/rust/websocket.rs +0 -23
- package/src/baseline/index.ts +0 -12
- package/src/baseline/tasks/cookie.ts +0 -22
- package/src/baseline/tasks/hashing.ts +0 -18
- package/src/baseline/tasks/hmac.ts +0 -38
- package/src/baseline/tasks/http.ts +0 -62
- package/src/baseline/tasks/json-patch.ts +0 -12
- package/src/baseline/tasks/json.ts +0 -50
- package/src/baseline/tasks/mime.ts +0 -5
- package/src/baseline/tasks/query.ts +0 -14
- package/src/baseline/tasks/token.ts +0 -15
- package/src/baseline/tasks/url.ts +0 -11
- package/src/baseline/tasks/validation.ts +0 -19
- package/src/baseline/tasks/websocket.ts +0 -14
- package/src/bench/assert.ts +0 -70
- package/src/bench/checks.ts +0 -227
- package/src/bench/checksum.ts +0 -27
- package/src/bench/comparisons.ts +0 -70
- package/src/bench/concurrent-worker.ts +0 -135
- package/src/bench/fixtures.ts +0 -233
- package/src/bench/index.ts +0 -2
- package/src/bench/measure.ts +0 -177
- package/src/bench/now.ts +0 -3
- package/src/bench/report.ts +0 -79
- package/src/bench/run.ts +0 -57
- package/src/bench/tasks/complex.ts +0 -232
- package/src/bench/tasks/concurrent.ts +0 -213
- package/src/bench/tasks/cookie.ts +0 -44
- package/src/bench/tasks/hashing.ts +0 -33
- package/src/bench/tasks/hmac.ts +0 -36
- package/src/bench/tasks/http.ts +0 -50
- package/src/bench/tasks/index.ts +0 -52
- package/src/bench/tasks/json-patch.ts +0 -21
- package/src/bench/tasks/json.ts +0 -33
- package/src/bench/tasks/mime.ts +0 -21
- package/src/bench/tasks/query.ts +0 -44
- package/src/bench/tasks/stress.ts +0 -142
- package/src/bench/tasks/token.ts +0 -20
- package/src/bench/tasks/url.ts +0 -34
- package/src/bench/tasks/validation.ts +0 -57
- package/src/bench/tasks/websocket.ts +0 -21
- package/src/bench/types.ts +0 -60
- package/src/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
// src/loader/ops.ts — Curated loader-op registry.
|
|
2
|
+
//
|
|
3
|
+
// Each op pairs a scalar FFI call (single item) with the packed batch call
|
|
4
|
+
// (bulk items → ONE native crossing). Result kinds mirror `rust.batch.*`:
|
|
5
|
+
//
|
|
6
|
+
// - "boolean" → single `boolean`, bulk bitset `Uint8Array` (0/1 per item)
|
|
7
|
+
// - "number" → single `number`, bulk `Uint32Array`
|
|
8
|
+
// - "bigint" → single `bigint`, bulk `BigInt64Array | BigUint64Array`
|
|
9
|
+
// - "bytes" → single `Uint8Array`, bulk `Uint8Array[]`
|
|
10
|
+
//
|
|
11
|
+
// Ops marked "paired" (see `LoaderOpSpec.pairedRest`) take a per-item
|
|
12
|
+
// companion in bulk form (e.g. `jsonPatch` = doc + patch). Ops whose scalar
|
|
13
|
+
// returns something richer than the batch bitset (`verifyCookie` returning the
|
|
14
|
+
// value, `jwtVerify` returning the decoded payload) are exposed as
|
|
15
|
+
// BOOLEAN-validity ops so single and bulk semantics stay identical — use
|
|
16
|
+
// `rust.<op>` directly for the decoded value.
|
|
17
|
+
//
|
|
18
|
+
// The bulk result shapes are intentionally IDENTICAL to `rust.batch.<op>` so
|
|
19
|
+
// the loader never pays a conversion cost on the hot path. This module is the
|
|
20
|
+
// single source of truth for what the loader can dispatch.
|
|
21
|
+
|
|
22
|
+
import { rust } from '../rust-ffi'
|
|
23
|
+
import { addon } from '../rust-ffi/addon'
|
|
24
|
+
import { decoder } from '../shared/bytes'
|
|
25
|
+
import type { PasswordHashOptions } from '../native'
|
|
26
|
+
import type { SchemaValidator } from '../shared/packed'
|
|
27
|
+
|
|
28
|
+
/** Empty bytes sentinel for skip-on-error scalar parity (e.g. AEAD decrypt). */
|
|
29
|
+
export const EMPTY_BYTES = new Uint8Array(0)
|
|
30
|
+
|
|
31
|
+
/** Result kinds supported by the loader (mirrors the packed batch wire shapes). */
|
|
32
|
+
export type LoaderResultKind = 'boolean' | 'number' | 'bigint' | 'bytes'
|
|
33
|
+
|
|
34
|
+
/** Single-item (scalar) result type for a kind. */
|
|
35
|
+
export type ScalarResult<K extends LoaderResultKind> = K extends 'boolean'
|
|
36
|
+
? boolean
|
|
37
|
+
: K extends 'number'
|
|
38
|
+
? number
|
|
39
|
+
: K extends 'bigint'
|
|
40
|
+
? bigint
|
|
41
|
+
: Uint8Array
|
|
42
|
+
|
|
43
|
+
/** Bulk (array-input) result type for a kind — same shape as `rust.batch.*`. */
|
|
44
|
+
export type BulkResult<K extends LoaderResultKind> = K extends 'boolean'
|
|
45
|
+
? Uint8Array
|
|
46
|
+
: K extends 'number'
|
|
47
|
+
? Uint32Array
|
|
48
|
+
: K extends 'bigint'
|
|
49
|
+
? BigInt64Array
|
|
50
|
+
: Uint8Array[]
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Bulk-result type overrides per op. Bigint ops whose hashes can set the high
|
|
54
|
+
* bit (e.g. `fnv1a64`) return an UNSIGNED `BigUint64Array` to match the scalar
|
|
55
|
+
* `rust.<op>`; the default signed `BigInt64Array` stays for sum-style ops.
|
|
56
|
+
*/
|
|
57
|
+
export interface LoaderOpBulkOverride {
|
|
58
|
+
fnv1a64: BigUint64Array
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Bulk result type of a specific op (per-op override wins). */
|
|
62
|
+
export type LoaderBulk<K extends LoaderOpName> = K extends keyof LoaderOpBulkOverride
|
|
63
|
+
? LoaderOpBulkOverride[K]
|
|
64
|
+
: BulkResult<LoaderOpKind<K>>
|
|
65
|
+
|
|
66
|
+
/** Every op the loader can dispatch. */
|
|
67
|
+
export type LoaderOpName =
|
|
68
|
+
| 'crc32'
|
|
69
|
+
| 'fnv1a64'
|
|
70
|
+
| 'jsonValid'
|
|
71
|
+
| 'jsonSumIds'
|
|
72
|
+
| 'validateEmail'
|
|
73
|
+
| 'validateUuid'
|
|
74
|
+
| 'validateIpv4'
|
|
75
|
+
| 'validateIpv6'
|
|
76
|
+
| 'queryParse'
|
|
77
|
+
| 'cookieParse'
|
|
78
|
+
| 'formParse'
|
|
79
|
+
| 'httpParseRequest'
|
|
80
|
+
| 'hexEncode'
|
|
81
|
+
| 'hexDecode'
|
|
82
|
+
| 'base64Encode'
|
|
83
|
+
| 'base64Decode'
|
|
84
|
+
| 'base64UrlEncode'
|
|
85
|
+
| 'base64UrlDecode'
|
|
86
|
+
| 'gzipCompress'
|
|
87
|
+
| 'gzipDecompress'
|
|
88
|
+
| 'brotliCompress'
|
|
89
|
+
| 'brotliDecompress'
|
|
90
|
+
| 'etag'
|
|
91
|
+
| 'urlEncode'
|
|
92
|
+
| 'urlDecode'
|
|
93
|
+
| 'urlDecodeBytes'
|
|
94
|
+
| 'wsAcceptKey'
|
|
95
|
+
| 'mimeFromExtension'
|
|
96
|
+
| 'signCookie'
|
|
97
|
+
| 'verifyCookie'
|
|
98
|
+
| 'csrfVerify'
|
|
99
|
+
| 'passwordHash'
|
|
100
|
+
| 'passwordVerify'
|
|
101
|
+
| 'hmacSha256'
|
|
102
|
+
| 'hmacSha256Verify'
|
|
103
|
+
| 'aeadEncrypt'
|
|
104
|
+
| 'aeadDecrypt'
|
|
105
|
+
| 'jwtSign'
|
|
106
|
+
| 'jwtVerify'
|
|
107
|
+
| 'jsonPatch'
|
|
108
|
+
| 'urlResolve'
|
|
109
|
+
| 'wsFrameEncode'
|
|
110
|
+
| 'sseEncode'
|
|
111
|
+
| 'schemaValidate'
|
|
112
|
+
|
|
113
|
+
/** Extra arguments (after the item/items) for each op — the SINGLE call shape. */
|
|
114
|
+
export interface LoaderOpArgs {
|
|
115
|
+
crc32: []
|
|
116
|
+
fnv1a64: []
|
|
117
|
+
jsonValid: []
|
|
118
|
+
jsonSumIds: []
|
|
119
|
+
validateEmail: []
|
|
120
|
+
validateUuid: []
|
|
121
|
+
validateIpv4: []
|
|
122
|
+
validateIpv6: []
|
|
123
|
+
queryParse: []
|
|
124
|
+
cookieParse: []
|
|
125
|
+
formParse: []
|
|
126
|
+
httpParseRequest: []
|
|
127
|
+
hexEncode: []
|
|
128
|
+
hexDecode: []
|
|
129
|
+
base64Encode: [urlSafe?: boolean, padding?: boolean]
|
|
130
|
+
base64Decode: [urlSafe?: boolean, padding?: boolean]
|
|
131
|
+
base64UrlEncode: []
|
|
132
|
+
base64UrlDecode: []
|
|
133
|
+
gzipCompress: [level?: number | null]
|
|
134
|
+
gzipDecompress: []
|
|
135
|
+
brotliCompress: [quality?: number | null]
|
|
136
|
+
brotliDecompress: []
|
|
137
|
+
etag: [weak?: boolean | null]
|
|
138
|
+
urlEncode: []
|
|
139
|
+
urlDecode: []
|
|
140
|
+
urlDecodeBytes: []
|
|
141
|
+
wsAcceptKey: []
|
|
142
|
+
mimeFromExtension: []
|
|
143
|
+
signCookie: [secret: Uint8Array]
|
|
144
|
+
/** Boolean-validity op: returns valid/invalid (use `rust.verifyCookie` for the value). */
|
|
145
|
+
verifyCookie: [secret: Uint8Array]
|
|
146
|
+
csrfVerify: [secret: Uint8Array]
|
|
147
|
+
passwordHash: [salt: Uint8Array, options?: PasswordHashOptions | null]
|
|
148
|
+
passwordVerify: [phc: Uint8Array]
|
|
149
|
+
hmacSha256: [key: Uint8Array]
|
|
150
|
+
hmacSha256Verify: [key: Uint8Array, sig: Uint8Array]
|
|
151
|
+
aeadEncrypt: [key: Uint8Array, nonce: Uint8Array, algorithm?: string | null]
|
|
152
|
+
aeadDecrypt: [key: Uint8Array, nonce: Uint8Array, algorithm?: string | null]
|
|
153
|
+
jwtSign: [secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number]
|
|
154
|
+
/** Boolean-validity op: returns valid/invalid (use `rust.jwtVerify` for the payload). */
|
|
155
|
+
jwtVerify: [secret: Uint8Array, nowSeconds?: number]
|
|
156
|
+
jsonPatch: [patch: Uint8Array]
|
|
157
|
+
urlResolve: [base: Uint8Array]
|
|
158
|
+
wsFrameEncode: [opcode: number, mask: boolean, fin: boolean]
|
|
159
|
+
sseEncode: [event?: string | null, id?: string | null, retry?: number | null]
|
|
160
|
+
schemaValidate: [validator: SchemaValidator]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Extra arguments for the BULK call shape. Paired ops take a per-item
|
|
165
|
+
* companion ARRAY here; every other op inherits the scalar `LoaderOpArgs`
|
|
166
|
+
* shape (companions are shared across the batch).
|
|
167
|
+
*/
|
|
168
|
+
export interface LoaderOpBulkArgs {
|
|
169
|
+
jsonPatch: [patches: Uint8Array[]]
|
|
170
|
+
hmacSha256Verify: [key: Uint8Array, sigs: Uint8Array[]]
|
|
171
|
+
passwordVerify: [phcs: Uint8Array[]]
|
|
172
|
+
urlResolve: [bases: Uint8Array[]]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Bulk call shape for any op (defaults to the scalar shape). */
|
|
176
|
+
export type LoaderBulkArgs<K extends LoaderOpName> = K extends keyof LoaderOpBulkArgs
|
|
177
|
+
? LoaderOpBulkArgs[K]
|
|
178
|
+
: LoaderOpArgs[K]
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Zero-alloc fast-bulk wiring used by the coalesced `load()` flush for
|
|
182
|
+
* boolean/number/bigint ops. The loader packs the inputs into a reusable
|
|
183
|
+
* scratch, calls `batch` (a native `_into` packed variant) to write the packed
|
|
184
|
+
* result straight into a reusable output buffer, then reads each element out
|
|
185
|
+
* of that buffer — no intermediate unpack array, no per-call native Vec.
|
|
186
|
+
*/
|
|
187
|
+
export interface LoaderFastSpec {
|
|
188
|
+
/** Write the packed batch result for `packed` into `output`; returns bytes written. */
|
|
189
|
+
batch(packed: Uint8Array, output: Uint8Array): number
|
|
190
|
+
/** Read the i-th scalar result out of the packed `output` written by `batch`. */
|
|
191
|
+
element(view: DataView, output: Uint8Array, index: number): boolean | number | bigint
|
|
192
|
+
/** Exact number of bytes `output` needs for `count` items. */
|
|
193
|
+
outSize(count: number): number
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Per-op runtime spec: single + bulk execution, plus element access. */
|
|
197
|
+
export interface LoaderOpSpec {
|
|
198
|
+
kind: LoaderResultKind
|
|
199
|
+
/** Single-item execution: `(input, ...rest) → scalar result`. */
|
|
200
|
+
scalar(input: Uint8Array, ...rest: unknown[]): unknown
|
|
201
|
+
/** Bulk execution: `(items, ...rest) → rust.batch-shaped result`. */
|
|
202
|
+
batch(items: Uint8Array[], ...rest: unknown[]): unknown
|
|
203
|
+
/** Read the i-th scalar result out of a bulk result. */
|
|
204
|
+
element(bulk: unknown, index: number): unknown
|
|
205
|
+
/**
|
|
206
|
+
* Zero-alloc fast bulk path for the coalesced `load()` flush. Ops with this
|
|
207
|
+
* MUST keep `element` returning the same per-item value as `fast.element`.
|
|
208
|
+
*/
|
|
209
|
+
fast?: LoaderFastSpec
|
|
210
|
+
/**
|
|
211
|
+
* Indices into the bulk `rest` args that are PER-ITEM companion arrays
|
|
212
|
+
* (paired ops). Used only by the adaptive scalar-loop fallback to split
|
|
213
|
+
* companions per item; the packed batch path passes them through unchanged.
|
|
214
|
+
*/
|
|
215
|
+
pairedRest?: number[]
|
|
216
|
+
/**
|
|
217
|
+
* Bigint ops whose bulk result is UNSIGNED (`BigUint64Array`). The adaptive
|
|
218
|
+
* scalar-loop fallback builds the same unsigned array so single/bulk parity
|
|
219
|
+
* holds for high-bit hashes.
|
|
220
|
+
*/
|
|
221
|
+
unsigned?: boolean
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function boolElem(bulk: unknown, index: number): boolean {
|
|
225
|
+
return ((bulk as Uint8Array)[index] ?? 0) === 1
|
|
226
|
+
}
|
|
227
|
+
function numElem(bulk: unknown, index: number): number {
|
|
228
|
+
return (bulk as Uint32Array)[index] as number
|
|
229
|
+
}
|
|
230
|
+
function bigElem(bulk: unknown, index: number): bigint {
|
|
231
|
+
return (bulk as BigInt64Array)[index] as bigint
|
|
232
|
+
}
|
|
233
|
+
function byteElem(bulk: unknown, index: number): Uint8Array {
|
|
234
|
+
return (bulk as Uint8Array[])[index] as Uint8Array
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── Zero-alloc fast-bulk readers (packed output → scalar) ──────────
|
|
238
|
+
|
|
239
|
+
function boolFast(_view: DataView, output: Uint8Array, index: number): boolean {
|
|
240
|
+
return (((output[4 + (index >> 3)] ?? 0) >> (index & 7)) & 1) === 1
|
|
241
|
+
}
|
|
242
|
+
function bitsetOutSize(count: number): number {
|
|
243
|
+
return 4 + Math.ceil(count / 8)
|
|
244
|
+
}
|
|
245
|
+
function u32Fast(view: DataView, _output: Uint8Array, index: number): number {
|
|
246
|
+
return view.getUint32(4 + index * 4, true)
|
|
247
|
+
}
|
|
248
|
+
function u32OutSize(count: number): number {
|
|
249
|
+
return 4 + count * 4
|
|
250
|
+
}
|
|
251
|
+
function i64Fast(view: DataView, _output: Uint8Array, index: number): bigint {
|
|
252
|
+
return view.getBigInt64(4 + index * 8, true)
|
|
253
|
+
}
|
|
254
|
+
function u64Fast(view: DataView, _output: Uint8Array, index: number): bigint {
|
|
255
|
+
return view.getBigUint64(4 + index * 8, true)
|
|
256
|
+
}
|
|
257
|
+
function i64OutSize(count: number): number {
|
|
258
|
+
return 4 + count * 8
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Native `_into` packed batch fns (lazy addon; resolved + cached on first use
|
|
262
|
+
// so the flush path skips the lazy-addon Proxy get after warm-up).
|
|
263
|
+
const fastNative = new Map<string, (packed: Uint8Array, output: Uint8Array) => number>()
|
|
264
|
+
function fastInto(name: string): (packed: Uint8Array, output: Uint8Array) => number {
|
|
265
|
+
let f = fastNative.get(name)
|
|
266
|
+
if (f === undefined) {
|
|
267
|
+
f = (addon as unknown as Record<string, (packed: Uint8Array, output: Uint8Array) => number>)[
|
|
268
|
+
name
|
|
269
|
+
] as (packed: Uint8Array, output: Uint8Array) => number
|
|
270
|
+
fastNative.set(name, f)
|
|
271
|
+
}
|
|
272
|
+
return f
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Packed FNV-1a 64 batch into a caller-owned buffer. The coalesced `load()`
|
|
277
|
+
* flush uses this to compute every deferred default cache key in ONE native
|
|
278
|
+
* crossing (`[u32 count][u64 LE × n]`, u64 bit pattern — matches the scalar
|
|
279
|
+
* `rust.fnv1a64` key). Returns bytes written.
|
|
280
|
+
*/
|
|
281
|
+
export const fnvBatchKeysInto = (packed: Uint8Array, output: Uint8Array): number =>
|
|
282
|
+
fastInto('fnv1A64BatchPackedInto')(packed, output)
|
|
283
|
+
|
|
284
|
+
const BOOLEAN = 'boolean'
|
|
285
|
+
const NUMBER = 'number'
|
|
286
|
+
const BIGINT = 'bigint'
|
|
287
|
+
const BYTES = 'bytes'
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Registry — single source of truth for loader-op wiring. Every op must have
|
|
291
|
+
* a scalar variant and a packed batch variant; ops whose scalar returns
|
|
292
|
+
* something other than the bulk element (e.g. `jwtVerify` returning the
|
|
293
|
+
* decoded payload, `verifyCookie` returning the value) are deliberately
|
|
294
|
+
* EXCLUDED so single and bulk semantics stay identical.
|
|
295
|
+
*/
|
|
296
|
+
export const LOADER_OPS = {
|
|
297
|
+
crc32: {
|
|
298
|
+
kind: NUMBER,
|
|
299
|
+
scalar: (i: Uint8Array) => rust.crc32(i),
|
|
300
|
+
batch: (items: Uint8Array[]) => rust.batch.crc32(items),
|
|
301
|
+
element: numElem,
|
|
302
|
+
fast: {
|
|
303
|
+
batch: (packed, output) => fastInto('crc32BatchPackedInto')(packed, output),
|
|
304
|
+
element: u32Fast,
|
|
305
|
+
outSize: u32OutSize,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
fnv1a64: {
|
|
309
|
+
kind: BIGINT,
|
|
310
|
+
unsigned: true,
|
|
311
|
+
scalar: (i: Uint8Array) => rust.fnv1a64(i),
|
|
312
|
+
batch: (items: Uint8Array[]) => rust.batch.fnv1a64(items),
|
|
313
|
+
element: bigElem,
|
|
314
|
+
fast: {
|
|
315
|
+
batch: (packed, output) => fastInto('fnv1A64BatchPackedInto')(packed, output),
|
|
316
|
+
element: u64Fast,
|
|
317
|
+
outSize: i64OutSize,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
jsonValid: {
|
|
321
|
+
kind: BOOLEAN,
|
|
322
|
+
scalar: (i: Uint8Array) => rust.jsonValid(i),
|
|
323
|
+
batch: (items: Uint8Array[]) => rust.batch.jsonValid(items),
|
|
324
|
+
element: boolElem,
|
|
325
|
+
fast: {
|
|
326
|
+
batch: (packed, output) => fastInto('jsonValidBatchPackedInto')(packed, output),
|
|
327
|
+
element: boolFast,
|
|
328
|
+
outSize: bitsetOutSize,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
jsonSumIds: {
|
|
332
|
+
kind: BIGINT,
|
|
333
|
+
scalar: (i: Uint8Array) => rust.jsonSumIds(i),
|
|
334
|
+
batch: (items: Uint8Array[]) => rust.batch.jsonSumIds(items),
|
|
335
|
+
element: bigElem,
|
|
336
|
+
fast: {
|
|
337
|
+
batch: (packed, output) => fastInto('jsonSumBatchPackedInto')(packed, output),
|
|
338
|
+
element: i64Fast,
|
|
339
|
+
outSize: i64OutSize,
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
validateEmail: {
|
|
343
|
+
kind: BOOLEAN,
|
|
344
|
+
scalar: (i: Uint8Array) => rust.validateEmail(i),
|
|
345
|
+
batch: (items: Uint8Array[]) => rust.batch.validateEmail(items),
|
|
346
|
+
element: boolElem,
|
|
347
|
+
fast: {
|
|
348
|
+
batch: (packed, output) => fastInto('validateEmailBatchPackedInto')(packed, output),
|
|
349
|
+
element: boolFast,
|
|
350
|
+
outSize: bitsetOutSize,
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
validateUuid: {
|
|
354
|
+
kind: BOOLEAN,
|
|
355
|
+
scalar: (i: Uint8Array) => rust.validateUuid(i),
|
|
356
|
+
batch: (items: Uint8Array[]) => rust.batch.validateUuid(items),
|
|
357
|
+
element: boolElem,
|
|
358
|
+
fast: {
|
|
359
|
+
batch: (packed, output) => fastInto('validateUuidBatchPackedInto')(packed, output),
|
|
360
|
+
element: boolFast,
|
|
361
|
+
outSize: bitsetOutSize,
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
validateIpv4: {
|
|
365
|
+
kind: BOOLEAN,
|
|
366
|
+
scalar: (i: Uint8Array) => rust.validateIpv4(i),
|
|
367
|
+
batch: (items: Uint8Array[]) => rust.batch.validateIpv4(items),
|
|
368
|
+
element: boolElem,
|
|
369
|
+
fast: {
|
|
370
|
+
batch: (packed, output) => fastInto('validateIpv4BatchPackedInto')(packed, output),
|
|
371
|
+
element: boolFast,
|
|
372
|
+
outSize: bitsetOutSize,
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
validateIpv6: {
|
|
376
|
+
kind: BOOLEAN,
|
|
377
|
+
scalar: (i: Uint8Array) => rust.validateIpv6(i),
|
|
378
|
+
batch: (items: Uint8Array[]) => rust.batch.validateIpv6(items),
|
|
379
|
+
element: boolElem,
|
|
380
|
+
fast: {
|
|
381
|
+
batch: (packed, output) => fastInto('validateIpv6BatchPackedInto')(packed, output),
|
|
382
|
+
element: boolFast,
|
|
383
|
+
outSize: bitsetOutSize,
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
queryParse: {
|
|
387
|
+
kind: BYTES,
|
|
388
|
+
scalar: (i: Uint8Array) => rust.queryParsePacked(i),
|
|
389
|
+
batch: (items: Uint8Array[]) => rust.batch.queryParse(items),
|
|
390
|
+
element: byteElem,
|
|
391
|
+
},
|
|
392
|
+
cookieParse: {
|
|
393
|
+
kind: BYTES,
|
|
394
|
+
scalar: (i: Uint8Array) => rust.cookieParsePacked(i),
|
|
395
|
+
batch: (items: Uint8Array[]) => rust.batch.cookieParse(items),
|
|
396
|
+
element: byteElem,
|
|
397
|
+
},
|
|
398
|
+
formParse: {
|
|
399
|
+
kind: BYTES,
|
|
400
|
+
scalar: (i: Uint8Array) => rust.formParsePacked(i),
|
|
401
|
+
batch: (items: Uint8Array[]) => rust.batch.formParse(items),
|
|
402
|
+
element: byteElem,
|
|
403
|
+
},
|
|
404
|
+
httpParseRequest: {
|
|
405
|
+
kind: BYTES,
|
|
406
|
+
scalar: (i: Uint8Array) => rust.httpParseRequestPacked(i),
|
|
407
|
+
batch: (items: Uint8Array[]) => rust.batch.httpParseRequest(items),
|
|
408
|
+
element: byteElem,
|
|
409
|
+
},
|
|
410
|
+
hexEncode: {
|
|
411
|
+
kind: BYTES,
|
|
412
|
+
scalar: (i: Uint8Array) => rust.hexEncode(i),
|
|
413
|
+
batch: (items: Uint8Array[]) => rust.batch.hexEncode(items),
|
|
414
|
+
element: byteElem,
|
|
415
|
+
},
|
|
416
|
+
hexDecode: {
|
|
417
|
+
kind: BYTES,
|
|
418
|
+
scalar: (i: Uint8Array) => rust.hexDecode(i),
|
|
419
|
+
batch: (items: Uint8Array[]) => rust.batch.hexDecode(items),
|
|
420
|
+
element: byteElem,
|
|
421
|
+
},
|
|
422
|
+
base64Encode: {
|
|
423
|
+
kind: BYTES,
|
|
424
|
+
scalar: (i: Uint8Array, urlSafe?: boolean, padding?: boolean) =>
|
|
425
|
+
rust.base64Encode(i, urlSafe, padding),
|
|
426
|
+
batch: (items: Uint8Array[], urlSafe?: boolean, padding?: boolean) =>
|
|
427
|
+
rust.batch.base64Encode(items, urlSafe, padding),
|
|
428
|
+
element: byteElem,
|
|
429
|
+
},
|
|
430
|
+
base64Decode: {
|
|
431
|
+
kind: BYTES,
|
|
432
|
+
scalar: (i: Uint8Array, urlSafe?: boolean, padding?: boolean) =>
|
|
433
|
+
rust.base64Decode(i, urlSafe, padding),
|
|
434
|
+
batch: (items: Uint8Array[], urlSafe?: boolean, padding?: boolean) =>
|
|
435
|
+
rust.batch.base64Decode(items, urlSafe, padding),
|
|
436
|
+
element: byteElem,
|
|
437
|
+
},
|
|
438
|
+
base64UrlEncode: {
|
|
439
|
+
kind: BYTES,
|
|
440
|
+
scalar: (i: Uint8Array) => rust.base64UrlEncode(i),
|
|
441
|
+
batch: (items: Uint8Array[]) => rust.batch.base64UrlEncode(items),
|
|
442
|
+
element: byteElem,
|
|
443
|
+
},
|
|
444
|
+
base64UrlDecode: {
|
|
445
|
+
kind: BYTES,
|
|
446
|
+
scalar: (i: Uint8Array) => rust.base64UrlDecode(i),
|
|
447
|
+
batch: (items: Uint8Array[]) => rust.batch.base64UrlDecode(items),
|
|
448
|
+
element: byteElem,
|
|
449
|
+
},
|
|
450
|
+
gzipCompress: {
|
|
451
|
+
kind: BYTES,
|
|
452
|
+
scalar: (i: Uint8Array, level?: number | null) => rust.gzipCompress(i, level),
|
|
453
|
+
batch: (items: Uint8Array[], level?: number | null) => rust.batch.gzipCompress(items, level),
|
|
454
|
+
element: byteElem,
|
|
455
|
+
},
|
|
456
|
+
gzipDecompress: {
|
|
457
|
+
kind: BYTES,
|
|
458
|
+
scalar: (i: Uint8Array) => rust.gzipDecompress(i),
|
|
459
|
+
batch: (items: Uint8Array[]) => rust.batch.gzipDecompress(items),
|
|
460
|
+
element: byteElem,
|
|
461
|
+
},
|
|
462
|
+
brotliCompress: {
|
|
463
|
+
kind: BYTES,
|
|
464
|
+
scalar: (i: Uint8Array, quality?: number | null) => rust.brotliCompress(i, quality),
|
|
465
|
+
batch: (items: Uint8Array[], quality?: number | null) =>
|
|
466
|
+
rust.batch.brotliCompress(items, quality),
|
|
467
|
+
element: byteElem,
|
|
468
|
+
},
|
|
469
|
+
brotliDecompress: {
|
|
470
|
+
kind: BYTES,
|
|
471
|
+
scalar: (i: Uint8Array) => rust.brotliDecompress(i),
|
|
472
|
+
batch: (items: Uint8Array[]) => rust.batch.brotliDecompress(items),
|
|
473
|
+
element: byteElem,
|
|
474
|
+
},
|
|
475
|
+
etag: {
|
|
476
|
+
kind: BYTES,
|
|
477
|
+
scalar: (i: Uint8Array, weak?: boolean | null) => rust.etag(i, weak ?? undefined),
|
|
478
|
+
batch: (items: Uint8Array[], weak?: boolean | null) => rust.batch.etag(items, weak),
|
|
479
|
+
element: byteElem,
|
|
480
|
+
},
|
|
481
|
+
urlEncode: {
|
|
482
|
+
kind: BYTES,
|
|
483
|
+
scalar: (i: Uint8Array) => rust.urlEncode(i),
|
|
484
|
+
batch: (items: Uint8Array[]) => rust.batch.urlEncode(items),
|
|
485
|
+
element: byteElem,
|
|
486
|
+
},
|
|
487
|
+
urlDecode: {
|
|
488
|
+
kind: BYTES,
|
|
489
|
+
scalar: (i: Uint8Array) => rust.urlDecode(i),
|
|
490
|
+
batch: (items: Uint8Array[]) => rust.batch.urlDecode(items),
|
|
491
|
+
element: byteElem,
|
|
492
|
+
},
|
|
493
|
+
urlDecodeBytes: {
|
|
494
|
+
kind: BYTES,
|
|
495
|
+
scalar: (i: Uint8Array) => rust.urlDecodeBytes(i),
|
|
496
|
+
batch: (items: Uint8Array[]) => rust.batch.urlDecodeBytes(items),
|
|
497
|
+
element: byteElem,
|
|
498
|
+
},
|
|
499
|
+
wsAcceptKey: {
|
|
500
|
+
kind: BYTES,
|
|
501
|
+
scalar: (i: Uint8Array) => rust.wsAcceptKey(i),
|
|
502
|
+
batch: (items: Uint8Array[]) => rust.batch.wsAcceptKey(items),
|
|
503
|
+
element: byteElem,
|
|
504
|
+
},
|
|
505
|
+
mimeFromExtension: {
|
|
506
|
+
kind: BYTES,
|
|
507
|
+
scalar: (i: Uint8Array) => rust.mimeFromExtension(i),
|
|
508
|
+
batch: (items: Uint8Array[]) => rust.batch.mimeFromExtension(items),
|
|
509
|
+
element: byteElem,
|
|
510
|
+
},
|
|
511
|
+
signCookie: {
|
|
512
|
+
kind: BYTES,
|
|
513
|
+
scalar: (i: Uint8Array, secret: Uint8Array) => rust.signCookie(i, secret),
|
|
514
|
+
batch: (items: Uint8Array[], secret: Uint8Array) => rust.batch.signCookie(items, secret),
|
|
515
|
+
element: byteElem,
|
|
516
|
+
},
|
|
517
|
+
// Boolean-validity op: single returns valid/invalid (not the decoded value).
|
|
518
|
+
// Use `rust.verifyCookie` when you need the signed value back.
|
|
519
|
+
verifyCookie: {
|
|
520
|
+
kind: BOOLEAN,
|
|
521
|
+
scalar: (i: Uint8Array, secret: Uint8Array) => rust.verifyCookie(i, secret) !== null,
|
|
522
|
+
batch: (items: Uint8Array[], secret: Uint8Array) => rust.batch.verifyCookie(items, secret),
|
|
523
|
+
element: boolElem,
|
|
524
|
+
},
|
|
525
|
+
csrfVerify: {
|
|
526
|
+
kind: BOOLEAN,
|
|
527
|
+
scalar: (i: Uint8Array, secret: Uint8Array) => rust.csrfVerify(i, secret),
|
|
528
|
+
batch: (items: Uint8Array[], secret: Uint8Array) => rust.batch.csrfVerify(items, secret),
|
|
529
|
+
element: boolElem,
|
|
530
|
+
},
|
|
531
|
+
passwordHash: {
|
|
532
|
+
kind: BYTES,
|
|
533
|
+
scalar: (i: Uint8Array, salt: Uint8Array, options?: PasswordHashOptions | null) =>
|
|
534
|
+
rust.passwordHash(i, salt, options),
|
|
535
|
+
batch: (items: Uint8Array[], salt: Uint8Array, options?: PasswordHashOptions | null) =>
|
|
536
|
+
rust.batch.passwordHash(items, salt, options),
|
|
537
|
+
element: byteElem,
|
|
538
|
+
},
|
|
539
|
+
passwordVerify: {
|
|
540
|
+
kind: BOOLEAN,
|
|
541
|
+
pairedRest: [0],
|
|
542
|
+
scalar: (i: Uint8Array, phc: Uint8Array) => rust.passwordVerify(i, phc),
|
|
543
|
+
batch: (items: Uint8Array[], phcs: Uint8Array[]) => rust.batch.passwordVerify(items, phcs),
|
|
544
|
+
element: boolElem,
|
|
545
|
+
},
|
|
546
|
+
aeadEncrypt: {
|
|
547
|
+
kind: BYTES,
|
|
548
|
+
scalar: (i: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null) =>
|
|
549
|
+
rust.aeadEncrypt(key, nonce, i, algorithm),
|
|
550
|
+
batch: (items: Uint8Array[], key: Uint8Array, nonce: Uint8Array, algorithm?: string | null) =>
|
|
551
|
+
rust.batch.aeadEncrypt(key, nonce, items, algorithm),
|
|
552
|
+
element: byteElem,
|
|
553
|
+
},
|
|
554
|
+
aeadDecrypt: {
|
|
555
|
+
kind: BYTES,
|
|
556
|
+
scalar: (i: Uint8Array, key: Uint8Array, nonce: Uint8Array, algorithm?: string | null) =>
|
|
557
|
+
rust.aeadDecrypt(key, nonce, i, algorithm) ?? EMPTY_BYTES,
|
|
558
|
+
batch: (items: Uint8Array[], key: Uint8Array, nonce: Uint8Array, algorithm?: string | null) =>
|
|
559
|
+
rust.batch.aeadDecrypt(key, nonce, items, algorithm),
|
|
560
|
+
element: byteElem,
|
|
561
|
+
},
|
|
562
|
+
jwtSign: {
|
|
563
|
+
kind: BYTES,
|
|
564
|
+
// Input is a JSON claim document (bytes); the scalar parses it to the
|
|
565
|
+
// claims object the native signer expects.
|
|
566
|
+
scalar: (i: Uint8Array, secret: Uint8Array, ttlSeconds?: number | null, nowSeconds?: number) =>
|
|
567
|
+
rust.jwtSign(
|
|
568
|
+
JSON.parse(decoder.decode(i)) as Record<string, unknown>,
|
|
569
|
+
secret,
|
|
570
|
+
ttlSeconds,
|
|
571
|
+
nowSeconds,
|
|
572
|
+
),
|
|
573
|
+
batch: (
|
|
574
|
+
items: Uint8Array[],
|
|
575
|
+
secret: Uint8Array,
|
|
576
|
+
ttlSeconds?: number | null,
|
|
577
|
+
nowSeconds?: number,
|
|
578
|
+
) => rust.batch.jwtSign(items, secret, ttlSeconds, nowSeconds),
|
|
579
|
+
element: byteElem,
|
|
580
|
+
},
|
|
581
|
+
// Boolean-validity op: single returns valid/invalid (not the payload).
|
|
582
|
+
// Use `rust.jwtVerify` when you need the decoded claims back.
|
|
583
|
+
jwtVerify: {
|
|
584
|
+
kind: BOOLEAN,
|
|
585
|
+
scalar: (i: Uint8Array, secret: Uint8Array, nowSeconds?: number) =>
|
|
586
|
+
rust.jwtVerify(i, secret, nowSeconds) !== null,
|
|
587
|
+
batch: (items: Uint8Array[], secret: Uint8Array, nowSeconds?: number) =>
|
|
588
|
+
rust.batch.jwtVerify(items, secret, nowSeconds ?? Math.floor(Date.now() / 1000)),
|
|
589
|
+
element: boolElem,
|
|
590
|
+
},
|
|
591
|
+
jsonPatch: {
|
|
592
|
+
kind: BYTES,
|
|
593
|
+
pairedRest: [0],
|
|
594
|
+
scalar: (i: Uint8Array, patch: Uint8Array) => rust.jsonPatch(i, patch),
|
|
595
|
+
batch: (items: Uint8Array[], patches: Uint8Array[]) => rust.batch.jsonPatch(items, patches),
|
|
596
|
+
element: byteElem,
|
|
597
|
+
},
|
|
598
|
+
urlResolve: {
|
|
599
|
+
kind: BYTES,
|
|
600
|
+
pairedRest: [0],
|
|
601
|
+
scalar: (i: Uint8Array, base: Uint8Array) => rust.urlResolve(base, i),
|
|
602
|
+
batch: (items: Uint8Array[], bases: Uint8Array[]) => rust.batch.urlResolve(bases, items),
|
|
603
|
+
element: byteElem,
|
|
604
|
+
},
|
|
605
|
+
wsFrameEncode: {
|
|
606
|
+
kind: BYTES,
|
|
607
|
+
scalar: (i: Uint8Array, opcode: number, mask: boolean, fin: boolean) =>
|
|
608
|
+
rust.wsFrameEncode(opcode, i, mask, fin),
|
|
609
|
+
batch: (items: Uint8Array[], opcode: number, mask: boolean, fin: boolean) =>
|
|
610
|
+
rust.batch.wsFrameEncode(items, opcode, mask, fin),
|
|
611
|
+
element: byteElem,
|
|
612
|
+
},
|
|
613
|
+
sseEncode: {
|
|
614
|
+
kind: BYTES,
|
|
615
|
+
scalar: (i: Uint8Array, event?: string | null, id?: string | null, retry?: number | null) =>
|
|
616
|
+
rust.sseEncodeEvent(event ?? null, i, id ?? null, retry ?? null),
|
|
617
|
+
batch: (
|
|
618
|
+
items: Uint8Array[],
|
|
619
|
+
event?: string | null,
|
|
620
|
+
id?: string | null,
|
|
621
|
+
retry?: number | null,
|
|
622
|
+
) => rust.batch.sseEncode(items, event, id, retry),
|
|
623
|
+
element: byteElem,
|
|
624
|
+
},
|
|
625
|
+
schemaValidate: {
|
|
626
|
+
kind: BOOLEAN,
|
|
627
|
+
scalar: (i: Uint8Array, validator: SchemaValidator) => validator.validate(i),
|
|
628
|
+
batch: (items: Uint8Array[], validator: SchemaValidator) =>
|
|
629
|
+
rust.batch.schemaValidate(validator, items),
|
|
630
|
+
element: boolElem,
|
|
631
|
+
},
|
|
632
|
+
hmacSha256: {
|
|
633
|
+
kind: BYTES,
|
|
634
|
+
// NOTE: scalar arg order is (key, data); the loader normalizes to
|
|
635
|
+
// (input, ...rest) so callers pass the key as the trailing argument.
|
|
636
|
+
scalar: (i: Uint8Array, key: Uint8Array) => rust.hmacSha256(key, i),
|
|
637
|
+
batch: (items: Uint8Array[], key: Uint8Array) => rust.batch.hmacSha256(items, key),
|
|
638
|
+
element: byteElem,
|
|
639
|
+
},
|
|
640
|
+
hmacSha256Verify: {
|
|
641
|
+
kind: BOOLEAN,
|
|
642
|
+
pairedRest: [1],
|
|
643
|
+
// scalar arg order is (key, data, sig); normalized to (input=data, key, sig).
|
|
644
|
+
scalar: (i: Uint8Array, key: Uint8Array, sig: Uint8Array) => rust.hmacSha256Verify(key, i, sig),
|
|
645
|
+
batch: (items: Uint8Array[], key: Uint8Array, sigs: Uint8Array[]) =>
|
|
646
|
+
rust.batch.hmacSha256Verify(items, sigs, key),
|
|
647
|
+
element: boolElem,
|
|
648
|
+
},
|
|
649
|
+
} as const satisfies Record<LoaderOpName, LoaderOpSpec>
|
|
650
|
+
|
|
651
|
+
/** Kind of a specific op. */
|
|
652
|
+
export type LoaderOpKind<K extends LoaderOpName> = (typeof LOADER_OPS)[K]['kind']
|
|
653
|
+
/** Single-item result type of a specific op. */
|
|
654
|
+
export type LoaderScalar<K extends LoaderOpName> = ScalarResult<LoaderOpKind<K>>
|
|
655
|
+
|
|
656
|
+
/** All op names, as a readonly array (for iteration). */
|
|
657
|
+
export const LOADER_OP_NAMES = Object.keys(LOADER_OPS) as LoaderOpName[]
|