ox 1.0.5 → 1.1.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/CHANGELOG.md +50 -0
- package/dist/core/Base64.d.ts +10 -21
- package/dist/core/Base64.d.ts.map +1 -1
- package/dist/core/Base64.js +8 -127
- package/dist/core/Base64.js.map +1 -1
- package/dist/core/BinaryStateTree.d.ts.map +1 -1
- package/dist/core/BinaryStateTree.js +2 -2
- package/dist/core/BinaryStateTree.js.map +1 -1
- package/dist/core/Bls.d.ts.map +1 -1
- package/dist/core/Bls.js +19 -44
- package/dist/core/Bls.js.map +1 -1
- package/dist/core/Ed25519.d.ts.map +1 -1
- package/dist/core/Ed25519.js +7 -6
- package/dist/core/Ed25519.js.map +1 -1
- package/dist/core/Engine.d.ts +124 -0
- package/dist/core/Engine.d.ts.map +1 -0
- package/dist/core/Engine.js +180 -0
- package/dist/core/Engine.js.map +1 -0
- package/dist/core/Hash.d.ts +4 -4
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +9 -12
- package/dist/core/Hash.js.map +1 -1
- package/dist/core/Keystore.d.ts.map +1 -1
- package/dist/core/Keystore.js +15 -10
- package/dist/core/Keystore.js.map +1 -1
- package/dist/core/Mnemonic.d.ts.map +1 -1
- package/dist/core/Mnemonic.js +3 -2
- package/dist/core/Mnemonic.js.map +1 -1
- package/dist/core/P256.d.ts.map +1 -1
- package/dist/core/P256.js +8 -9
- package/dist/core/P256.js.map +1 -1
- package/dist/core/Secp256k1.d.ts.map +1 -1
- package/dist/core/Secp256k1.js +8 -9
- package/dist/core/Secp256k1.js.map +1 -1
- package/dist/core/WebCryptoP256.d.ts.map +1 -1
- package/dist/core/WebCryptoP256.js +2 -2
- package/dist/core/WebCryptoP256.js.map +1 -1
- package/dist/core/X25519.d.ts.map +1 -1
- package/dist/core/X25519.js +4 -3
- package/dist/core/X25519.js.map +1 -1
- package/dist/core/internal/bls.d.ts +12 -0
- package/dist/core/internal/bls.d.ts.map +1 -0
- package/dist/core/internal/bls.js +79 -0
- package/dist/core/internal/bls.js.map +1 -0
- package/dist/core/internal/codec/base64.d.ts +84 -0
- package/dist/core/internal/codec/base64.d.ts.map +1 -0
- package/dist/core/internal/codec/base64.js +177 -0
- package/dist/core/internal/codec/base64.js.map +1 -0
- package/dist/core/internal/codec/hex.d.ts +49 -4
- package/dist/core/internal/codec/hex.d.ts.map +1 -1
- package/dist/core/internal/codec/hex.js +109 -35
- package/dist/core/internal/codec/hex.js.map +1 -1
- package/dist/core/internal/curves.d.ts +17 -0
- package/dist/core/internal/curves.d.ts.map +1 -0
- package/dist/core/internal/curves.js +17 -0
- package/dist/core/internal/curves.js.map +1 -0
- package/dist/core/internal/ed25519.d.ts +14 -0
- package/dist/core/internal/ed25519.d.ts.map +1 -0
- package/dist/core/internal/ed25519.js +29 -0
- package/dist/core/internal/ed25519.js.map +1 -0
- package/dist/core/internal/engine.d.ts +278 -0
- package/dist/core/internal/engine.d.ts.map +1 -0
- package/dist/core/internal/engine.js +131 -0
- package/dist/core/internal/engine.js.map +1 -0
- package/dist/core/internal/hash.d.ts +12 -0
- package/dist/core/internal/hash.d.ts.map +1 -0
- package/dist/core/internal/hash.js +30 -0
- package/dist/core/internal/hash.js.map +1 -0
- package/dist/core/internal/keystore.d.ts +14 -0
- package/dist/core/internal/keystore.d.ts.map +1 -0
- package/dist/core/internal/keystore.js +32 -0
- package/dist/core/internal/keystore.js.map +1 -0
- package/dist/core/internal/mnemonic.d.ts +4 -0
- package/dist/core/internal/mnemonic.d.ts.map +1 -0
- package/dist/core/internal/mnemonic.js +14 -0
- package/dist/core/internal/mnemonic.js.map +1 -0
- package/dist/core/internal/p256.d.ts +14 -0
- package/dist/core/internal/p256.d.ts.map +1 -0
- package/dist/core/internal/p256.js +36 -0
- package/dist/core/internal/p256.js.map +1 -0
- package/dist/core/internal/secp256k1.d.ts +14 -0
- package/dist/core/internal/secp256k1.d.ts.map +1 -0
- package/dist/core/internal/secp256k1.js +36 -0
- package/dist/core/internal/secp256k1.js.map +1 -0
- package/dist/core/internal/x25519.d.ts +8 -0
- package/dist/core/internal/x25519.d.ts.map +1 -0
- package/dist/core/internal/x25519.js +20 -0
- package/dist/core/internal/x25519.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.docs.d.ts +2 -0
- package/dist/index.docs.d.ts.map +1 -1
- package/dist/index.docs.js +2 -0
- package/dist/index.docs.js.map +1 -1
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -1
- package/dist/node/Engine.d.ts +55 -0
- package/dist/node/Engine.d.ts.map +1 -0
- package/dist/node/Engine.js +51 -0
- package/dist/node/Engine.js.map +1 -0
- package/dist/node/Hash.d.ts +39 -0
- package/dist/node/Hash.d.ts.map +1 -0
- package/dist/node/Hash.js +37 -0
- package/dist/node/Hash.js.map +1 -0
- package/dist/node/index.d.ts +39 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +39 -0
- package/dist/node/index.js.map +1 -0
- package/dist/tempo/VirtualMaster.d.ts +2 -0
- package/dist/tempo/VirtualMaster.d.ts.map +1 -1
- package/dist/tempo/VirtualMaster.js +19 -5
- package/dist/tempo/VirtualMaster.js.map +1 -1
- package/dist/tempo/internal/mine.wasm.d.ts +2 -4
- package/dist/tempo/internal/mine.wasm.d.ts.map +1 -1
- package/dist/tempo/internal/mine.wasm.js +7 -12
- package/dist/tempo/internal/mine.wasm.js.map +1 -1
- package/dist/tempo/internal/virtualMasterPool.js +4 -1
- package/dist/tempo/internal/virtualMasterPool.js.map +1 -1
- package/dist/wasm/Engine.d.ts +62 -0
- package/dist/wasm/Engine.d.ts.map +1 -0
- package/dist/wasm/Engine.js +57 -0
- package/dist/wasm/Engine.js.map +1 -0
- package/dist/wasm/Hash.d.ts +49 -0
- package/dist/wasm/Hash.d.ts.map +1 -0
- package/dist/wasm/Hash.js +70 -0
- package/dist/wasm/Hash.js.map +1 -0
- package/dist/wasm/index.d.ts +46 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/dist/wasm/index.js +46 -0
- package/dist/wasm/index.js.map +1 -0
- package/dist/wasm/internal/hash.d.ts +9 -0
- package/dist/wasm/internal/hash.d.ts.map +1 -0
- package/dist/wasm/internal/hash.js +26 -0
- package/dist/wasm/internal/hash.js.map +1 -0
- package/dist/wasm/internal/hashes.wasm.d.ts +5 -0
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/hashes.wasm.js +12 -0
- package/dist/wasm/internal/hashes.wasm.js.map +1 -0
- package/dist/wasm/internal/instantiate.d.ts +56 -0
- package/dist/wasm/internal/instantiate.d.ts.map +1 -0
- package/dist/wasm/internal/instantiate.js +92 -0
- package/dist/wasm/internal/instantiate.js.map +1 -0
- package/dist/zod/TypedData.d.ts +12 -12
- package/package.json +36 -1
- package/src/core/Base64.ts +19 -166
- package/src/core/BinaryStateTree.ts +2 -3
- package/src/core/Bls.ts +27 -77
- package/src/core/Ed25519.ts +7 -7
- package/src/core/Engine.ts +235 -0
- package/src/core/Hash.ts +9 -12
- package/src/core/Keystore.ts +17 -17
- package/src/core/Mnemonic.ts +3 -6
- package/src/core/P256.ts +15 -24
- package/src/core/Rlp_tx.bench.ts +2 -4
- package/src/core/Secp256k1.ts +9 -14
- package/src/core/TxEnvelopeEip1559.bench.ts +0 -5
- package/src/core/WebCryptoP256.ts +2 -2
- package/src/core/X25519.ts +4 -3
- package/src/core/_test/AbiParameters.adversarial.fuzz.ts +1 -2
- package/src/core/_test/AbiParameters.fuzz.ts +1 -2
- package/src/core/_test/Base.fuzz.ts +1 -2
- package/src/core/_test/Cbor.fuzz.ts +1 -2
- package/src/core/_test/Engine.fuzz.ts +293 -0
- package/src/core/_test/Engine.test-d.ts +84 -0
- package/src/core/_test/Engine.test.ts +523 -0
- package/src/core/_test/Hash.vectors.test.ts +56 -0
- package/src/core/_test/Hex.fuzz.ts +1 -2
- package/src/core/_test/Rlp.fuzz.ts +1 -2
- package/src/core/_test/Signature.fuzz.ts +1 -2
- package/src/core/_test/curves.test.ts +7 -0
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/bls.ts +121 -0
- package/src/core/internal/codec/_test/base64.conformance.ts +156 -0
- package/src/core/internal/codec/_test/hex.conformance.ts +204 -0
- package/src/core/internal/codec/_test/hex.fuzz.ts +234 -0
- package/src/core/internal/codec/_test/hex.test.ts +124 -0
- package/src/core/internal/codec/base64.ts +222 -0
- package/src/core/internal/codec/hex.ts +115 -34
- package/src/core/internal/curves.ts +18 -0
- package/src/core/internal/ed25519.ts +64 -0
- package/src/core/internal/engine.ts +410 -0
- package/src/core/internal/hash.ts +48 -0
- package/src/core/internal/keystore.ts +107 -0
- package/src/core/internal/mnemonic.ts +18 -0
- package/src/core/internal/p256.ts +91 -0
- package/src/core/internal/secp256k1.ts +91 -0
- package/src/core/internal/x25519.ts +40 -0
- package/src/index.docs.ts +2 -0
- package/src/index.ts +36 -0
- package/src/node/Engine.ts +69 -0
- package/src/node/Hash.ts +56 -0
- package/src/node/_test/Engine.test.ts +62 -0
- package/src/node/_test/Hash.test-d.ts +26 -0
- package/src/node/_test/Hash.test.ts +102 -0
- package/src/node/index.ts +40 -0
- package/src/tempo/VirtualMaster.test.ts +75 -0
- package/src/tempo/VirtualMaster.ts +28 -6
- package/src/tempo/internal/mine.wasm.ts +7 -13
- package/src/tempo/internal/virtualMasterPool.ts +4 -1
- package/src/tsconfig.json +2 -1
- package/src/version.ts +1 -1
- package/src/wasm/Engine.ts +76 -0
- package/src/wasm/Hash.bench.ts +60 -0
- package/src/wasm/Hash.ts +109 -0
- package/src/wasm/_test/Engine.test.ts +62 -0
- package/src/wasm/_test/Hash.browser.test.ts +50 -0
- package/src/wasm/_test/Hash.fuzz.ts +188 -0
- package/src/wasm/_test/Hash.test-d.ts +28 -0
- package/src/wasm/_test/Hash.test.ts +211 -0
- package/src/wasm/_test/Hash.vectors.test.ts +117 -0
- package/src/wasm/_test/Runtime.test.ts +21 -0
- package/src/wasm/_test/runtime.wasm.ts +11 -0
- package/src/wasm/index.ts +47 -0
- package/src/wasm/internal/hash.ts +50 -0
- package/src/wasm/internal/hashes.wasm.ts +14 -0
- package/src/wasm/internal/instantiate.ts +135 -0
- package/src/tempo/internal/mine.c +0 -182
|
@@ -19,40 +19,92 @@ const nibbleTable = /*#__PURE__*/ (() => {
|
|
|
19
19
|
return table
|
|
20
20
|
})()
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
22
|
+
// Native fast-path detection. `Uint8Array.prototype.toHex` ships in Chromium
|
|
23
|
+
// 145, Safari 18.2+ and Firefox 133+, but not in Node 24, which reaches
|
|
24
|
+
// `Buffer` instead.
|
|
25
|
+
//
|
|
26
|
+
// Only *encoding* delegates to it. Encoding is total, so a native method has
|
|
27
|
+
// nothing to get wrong; decoding has to reject, and the native decoders do not
|
|
28
|
+
// do so reliably -- Chromium 145 reads U+C230 as the digit `0`, masking the
|
|
29
|
+
// code unit to 8 bits exactly as `Buffer` does, and only rejects it from 149
|
|
30
|
+
// on. test262 misses this because none of its illegal characters mask onto the
|
|
31
|
+
// hex alphabet. Guarding `fromHex` costs an ASCII scan that leaves it only
|
|
32
|
+
// 10-25% ahead of the loop, so it is not used at all.
|
|
25
33
|
const _Buffer: typeof globalThis.Buffer | undefined = (
|
|
26
34
|
globalThis as typeof globalThis & { Buffer?: typeof globalThis.Buffer }
|
|
27
35
|
).Buffer
|
|
28
36
|
const nativeToHex: ((this: Uint8Array) => string) | undefined = (
|
|
29
37
|
Uint8Array.prototype as Uint8Array & { toHex?: () => string }
|
|
30
38
|
).toHex
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Byte count below which {@link hexToBytesLoop} beats `Buffer.from(…, 'hex')`,
|
|
42
|
+
* whose fixed per-call cost short inputs cannot amortize. Measured on Node 24
|
|
43
|
+
* (V8 13.6): the crossover sits between 32 and 48 bytes, and 32 covers the
|
|
44
|
+
* sizes Ethereum work is made of -- 20-byte addresses and 32-byte words.
|
|
45
|
+
*
|
|
46
|
+
* `Buffer` is the only tier this applies to; where it is absent the loop
|
|
47
|
+
* decodes at every size. Re-measure before moving it.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
const loopDecodeMaxBytes = 32
|
|
34
52
|
|
|
35
53
|
/**
|
|
36
54
|
* Encodes a `Uint8Array` into a `0x`-prefixed lowercase hex string. Uses the
|
|
37
|
-
* native `Uint8Array.prototype.toHex`
|
|
38
|
-
*
|
|
55
|
+
* native `Uint8Array.prototype.toHex` or Node's `Buffer` when available;
|
|
56
|
+
* otherwise a tight JS loop.
|
|
39
57
|
*
|
|
40
58
|
* @internal
|
|
41
59
|
*/
|
|
42
60
|
export function bytesToHex(value: Uint8Array): Hex {
|
|
43
|
-
if (
|
|
44
|
-
if (
|
|
45
|
-
|
|
61
|
+
if (bytesToHexNative) return bytesToHexNative(value)
|
|
62
|
+
if (bytesToHexBuffer) return bytesToHexBuffer(value)
|
|
63
|
+
return bytesToHexLoop(value)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Encodes via `Uint8Array.prototype.toHex`, where the runtime has it.
|
|
68
|
+
* `undefined` otherwise, so callers and the conformance suite can both ask
|
|
69
|
+
* whether this tier exists rather than inferring it.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export const bytesToHexNative: ((value: Uint8Array) => Hex) | undefined =
|
|
74
|
+
nativeToHex && ((value) => `0x${nativeToHex.call(value)}` as Hex)
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Encodes via Node's `Buffer`, where the runtime has it.
|
|
78
|
+
*
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export const bytesToHexBuffer: ((value: Uint8Array) => Hex) | undefined =
|
|
82
|
+
_Buffer &&
|
|
83
|
+
((value) =>
|
|
84
|
+
`0x${_Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString('hex')}` as Hex)
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Encodes in plain JavaScript, for runtimes with neither the native method nor
|
|
88
|
+
* `Buffer`: browsers predating `Uint8Array.prototype.toHex`, which is recent
|
|
89
|
+
* enough that a meaningful share of installed browsers still land here.
|
|
90
|
+
*
|
|
91
|
+
* Appending to a string beats collecting substrings and joining them: V8 builds
|
|
92
|
+
* a rope and flattens it once, where `join` over an `Array.from({ length })`
|
|
93
|
+
* pays for a holey array as well as the concatenation. Measured at ~6.5x on a
|
|
94
|
+
* 32-byte input, counting the flatten.
|
|
95
|
+
*
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
export function bytesToHexLoop(value: Uint8Array): Hex {
|
|
46
99
|
const length = value.length
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < length; i++)
|
|
49
|
-
return
|
|
100
|
+
let hex = '0x'
|
|
101
|
+
for (let i = 0; i < length; i++) hex += hexes[value[i]!]
|
|
102
|
+
return hex as Hex
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
/**
|
|
53
106
|
* Strictly decodes a `0x`-prefixed even-length hex string into a `Uint8Array`.
|
|
54
|
-
* Uses
|
|
55
|
-
* Node's `Buffer` when available; otherwise a tight JS loop.
|
|
107
|
+
* Uses a JS loop for short inputs and Node's `Buffer` for longer ones.
|
|
56
108
|
*
|
|
57
109
|
* @internal
|
|
58
110
|
*/
|
|
@@ -64,43 +116,72 @@ export function hexToBytes(value: string): Uint8Array {
|
|
|
64
116
|
value.charCodeAt(1) !== 120 /* 'x' */
|
|
65
117
|
)
|
|
66
118
|
throw new InvalidHexValueError(value)
|
|
67
|
-
const
|
|
68
|
-
if ((
|
|
119
|
+
const nibbles = value.length - 2
|
|
120
|
+
if ((nibbles & 1) !== 0) throw new InvalidLengthError(value as Hex)
|
|
121
|
+
const length = nibbles >> 1
|
|
69
122
|
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
123
|
+
if (length <= loopDecodeMaxBytes) return hexToBytesLoop(value, length)
|
|
124
|
+
if (hexToBytesBuffer) return hexToBytesBuffer(value, length)
|
|
125
|
+
return hexToBytesLoop(value, length)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Decodes via Node's `Buffer`, where the runtime has it.
|
|
130
|
+
*
|
|
131
|
+
* @internal
|
|
132
|
+
*/
|
|
133
|
+
export const hexToBytesBuffer:
|
|
134
|
+
| ((value: string, length: number) => Uint8Array)
|
|
135
|
+
| undefined =
|
|
136
|
+
_Buffer &&
|
|
137
|
+
((value, length) => {
|
|
138
|
+
const body = value.slice(2)
|
|
139
|
+
// `Buffer.from(…, 'hex')` masks each UTF-16 code unit to 8 bits, so a
|
|
140
|
+
// character above U+00FF can alias a hex digit: U+C230 masks to 0x30, the
|
|
141
|
+
// digit `0`, and `'0x숰0'` decodes to `0x00` instead of being rejected.
|
|
142
|
+
// Masking cannot fabricate a digit from pure ASCII, and a string is pure
|
|
143
|
+
// ASCII exactly when its UTF-8 length matches its code-unit count.
|
|
144
|
+
if (_Buffer.byteLength(body, 'utf8') !== body.length)
|
|
74
145
|
throw new InvalidHexValueError(value)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (_Buffer && body.length > 0) {
|
|
78
146
|
// Buffer.from with 'hex' silently truncates on invalid chars; verify
|
|
79
147
|
// byteLength matches expectations to detect malformed input.
|
|
80
|
-
const expected = body.length >> 1
|
|
81
148
|
const buf = _Buffer.from(body, 'hex')
|
|
82
|
-
if (buf.length !==
|
|
149
|
+
if (buf.length !== length) throw new InvalidHexValueError(value)
|
|
83
150
|
// Copy out of Buffer pool: callers may rely on `.buffer` being a
|
|
84
151
|
// standalone ArrayBuffer (e.g. WebAuthn attestationObject round-trips).
|
|
85
152
|
const out = new Uint8Array(buf.byteLength)
|
|
86
153
|
out.set(buf)
|
|
87
154
|
return out
|
|
88
|
-
}
|
|
155
|
+
})
|
|
89
156
|
|
|
90
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Decodes in plain JavaScript, reading nibbles straight out of `value` from
|
|
159
|
+
* index 2 so no substring is materialized.
|
|
160
|
+
*
|
|
161
|
+
* Invalidity is accumulated rather than branched on per nibble: a valid nibble
|
|
162
|
+
* is at most `0x0f`, so only the `0xff` sentinel can set bit 7, and one check
|
|
163
|
+
* after the loop covers every character. `nibbleTable` only spans Latin-1, so a
|
|
164
|
+
* code unit above it reads `undefined` and has to be mapped to the sentinel --
|
|
165
|
+
* without that, a non-ASCII character would OR in as zero and decode silently.
|
|
166
|
+
*
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
export function hexToBytesLoop(value: string, length: number): Uint8Array {
|
|
91
170
|
const out = new Uint8Array(length)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
171
|
+
let invalid = 0
|
|
172
|
+
for (let i = 0, j = 2; i < length; i++) {
|
|
173
|
+
const hi = nibbleTable[value.charCodeAt(j++)] ?? 0xff
|
|
174
|
+
const lo = nibbleTable[value.charCodeAt(j++)] ?? 0xff
|
|
175
|
+
invalid |= hi | lo
|
|
96
176
|
out[i] = (hi << 4) | lo
|
|
97
177
|
}
|
|
178
|
+
if (invalid & 0x80) throw new InvalidHexValueError(value)
|
|
98
179
|
return out
|
|
99
180
|
}
|
|
100
181
|
|
|
101
182
|
/** @internal */
|
|
102
183
|
export function charCodeToBase16(char: number): number | undefined {
|
|
103
|
-
const v = nibbleTable[char]
|
|
184
|
+
const v = nibbleTable[char] ?? 0xff
|
|
104
185
|
return v === 0xff ? undefined : v
|
|
105
186
|
}
|
|
106
187
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curve constants, inlined so that modules needing nothing but an order do not
|
|
3
|
+
* pull a curve implementation into the bundle.
|
|
4
|
+
*
|
|
5
|
+
* These are fixed by the curve specifications and are asserted against
|
|
6
|
+
* `@noble/curves` in `src/core/_test/curves.test.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Order of the secp256r1 (P-256) curve.
|
|
13
|
+
*
|
|
14
|
+
* @see https://www.secg.org/sec2-v2.pdf
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export const p256N =
|
|
18
|
+
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519.js'
|
|
2
|
+
import { type Complete, type Eddsa, overrides } from './engine.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolvers for the `Ed25519` slot, and ox's defaults for it, backed by
|
|
6
|
+
* `@noble/curves`.
|
|
7
|
+
*
|
|
8
|
+
* Declaring the defaults against the slot contract is what keeps them honest: a
|
|
9
|
+
* default that goes missing, or whose signature drifts, fails to compile rather
|
|
10
|
+
* than failing at the call site.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const getPublicKeyDefault: Complete<Eddsa>['getPublicKey'] = (privateKey) =>
|
|
14
|
+
ed25519.getPublicKey(privateKey)
|
|
15
|
+
|
|
16
|
+
const randomSecretKeyDefault: Complete<Eddsa>['randomSecretKey'] = () =>
|
|
17
|
+
ed25519.utils.randomSecretKey()
|
|
18
|
+
|
|
19
|
+
const signDefault: Complete<Eddsa>['sign'] = (payload, privateKey) =>
|
|
20
|
+
ed25519.sign(payload, privateKey)
|
|
21
|
+
|
|
22
|
+
const toMontgomeryDefault: Complete<Eddsa>['toMontgomery'] = (publicKey) =>
|
|
23
|
+
ed25519.utils.toMontgomery(publicKey)
|
|
24
|
+
|
|
25
|
+
const toMontgomerySecretDefault: Complete<Eddsa>['toMontgomerySecret'] = (
|
|
26
|
+
privateKey,
|
|
27
|
+
) => ed25519.utils.toMontgomerySecret(privateKey)
|
|
28
|
+
|
|
29
|
+
const verifyDefault: Complete<Eddsa>['verify'] = (
|
|
30
|
+
signature,
|
|
31
|
+
payload,
|
|
32
|
+
publicKey,
|
|
33
|
+
) => ed25519.verify(signature, payload, publicKey)
|
|
34
|
+
|
|
35
|
+
/** @internal */
|
|
36
|
+
export const getPublicKey: Complete<Eddsa>['getPublicKey'] = (privateKey) =>
|
|
37
|
+
(overrides.Ed25519?.getPublicKey ?? getPublicKeyDefault)(privateKey)
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
export const randomSecretKey: Complete<Eddsa>['randomSecretKey'] = () =>
|
|
41
|
+
(overrides.Ed25519?.randomSecretKey ?? randomSecretKeyDefault)()
|
|
42
|
+
|
|
43
|
+
/** @internal */
|
|
44
|
+
export const sign: Complete<Eddsa>['sign'] = (payload, privateKey) =>
|
|
45
|
+
(overrides.Ed25519?.sign ?? signDefault)(payload, privateKey)
|
|
46
|
+
|
|
47
|
+
/** @internal */
|
|
48
|
+
export const toMontgomery: Complete<Eddsa>['toMontgomery'] = (publicKey) =>
|
|
49
|
+
(overrides.Ed25519?.toMontgomery ?? toMontgomeryDefault)(publicKey)
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
export const toMontgomerySecret: Complete<Eddsa>['toMontgomerySecret'] = (
|
|
53
|
+
privateKey,
|
|
54
|
+
) =>
|
|
55
|
+
(overrides.Ed25519?.toMontgomerySecret ?? toMontgomerySecretDefault)(
|
|
56
|
+
privateKey,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
/** @internal */
|
|
60
|
+
export const verify: Complete<Eddsa>['verify'] = (
|
|
61
|
+
signature,
|
|
62
|
+
payload,
|
|
63
|
+
publicKey,
|
|
64
|
+
) => (overrides.Ed25519?.verify ?? verifyDefault)(signature, payload, publicKey)
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine slot contracts and the override registry.
|
|
3
|
+
*
|
|
4
|
+
* This module intentionally has **no imports**. It is pulled into the module
|
|
5
|
+
* graph of every crypto module in ox, so it must not drag any dependency along
|
|
6
|
+
* with it. That constraint is also why the contracts speak raw `Uint8Array`
|
|
7
|
+
* rather than `Bytes.Bytes`/`Hex.Hex` -- coercion stays in the public module,
|
|
8
|
+
* and the engine boundary is plain bytes in, plain bytes out.
|
|
9
|
+
*
|
|
10
|
+
* The registry holds **overrides only**. Defaults are never registered here,
|
|
11
|
+
* because a registered default has to live on a complete slot object, and a
|
|
12
|
+
* slot object is a single reachable value: esbuild cannot drop the properties
|
|
13
|
+
* of one, so every default's `@noble/*` import survives into every bundle that
|
|
14
|
+
* touches the slot. Measured on a keccak256-only bundle, that costs 5 kB gzip.
|
|
15
|
+
* Instead each slot has a resolver module under `internal/` holding one binding
|
|
16
|
+
* per primitive, and the fallback is chosen there, per primitive.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hash primitives. One-shot, synchronous, bytes in / bytes out.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export type Hash = {
|
|
27
|
+
/** BLAKE3, 32-byte digest. */
|
|
28
|
+
blake3?: ((input: Uint8Array) => Uint8Array) | undefined
|
|
29
|
+
/** HMAC-SHA256. */
|
|
30
|
+
hmacSha256?:
|
|
31
|
+
| ((key: Uint8Array, message: Uint8Array) => Uint8Array)
|
|
32
|
+
| undefined
|
|
33
|
+
/** Keccak256 (not FIPS-202 SHA3-256). */
|
|
34
|
+
keccak256?: ((input: Uint8Array) => Uint8Array) | undefined
|
|
35
|
+
/** RIPEMD-160. */
|
|
36
|
+
ripemd160?: ((input: Uint8Array) => Uint8Array) | undefined
|
|
37
|
+
/** SHA-256. */
|
|
38
|
+
sha256?: ((input: Uint8Array) => Uint8Array) | undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Short-Weierstrass ECDSA. Shared by the `Secp256k1` and `P256` slots.
|
|
43
|
+
*
|
|
44
|
+
* Signatures are always low-S normalized and always returned in 65-byte
|
|
45
|
+
* recovered form -- the contract mandates it, so an engine cannot silently opt
|
|
46
|
+
* out.
|
|
47
|
+
*
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export type Ecdsa = {
|
|
51
|
+
/** Derives an uncompressed (65-byte) public key. */
|
|
52
|
+
getPublicKey?: ((privateKey: Uint8Array) => Uint8Array) | undefined
|
|
53
|
+
/**
|
|
54
|
+
* Computes a compressed (33-byte) ECDH shared secret.
|
|
55
|
+
*
|
|
56
|
+
* `publicKey` arrives in whichever SEC1 form the caller supplied, so an
|
|
57
|
+
* implementation has to accept both the compressed 33-byte and uncompressed
|
|
58
|
+
* 65-byte encodings.
|
|
59
|
+
*/
|
|
60
|
+
getSharedSecret?:
|
|
61
|
+
| ((privateKey: Uint8Array, publicKey: Uint8Array) => Uint8Array)
|
|
62
|
+
| undefined
|
|
63
|
+
/**
|
|
64
|
+
* Recovers an uncompressed public key from a 65-byte `yParity || r || s`
|
|
65
|
+
* signature, the same layout {@link Ecdsa.sign} returns.
|
|
66
|
+
*/
|
|
67
|
+
recoverPublicKey?:
|
|
68
|
+
| ((signature: Uint8Array, payload: Uint8Array) => Uint8Array)
|
|
69
|
+
| undefined
|
|
70
|
+
/** Generates a random private key. */
|
|
71
|
+
randomSecretKey?: (() => Uint8Array) | undefined
|
|
72
|
+
/** Signs a payload, returning 65 bytes of `yParity || r || s`. */
|
|
73
|
+
sign?:
|
|
74
|
+
| ((
|
|
75
|
+
payload: Uint8Array,
|
|
76
|
+
privateKey: Uint8Array,
|
|
77
|
+
options: {
|
|
78
|
+
/** Extra entropy for hedged signing, or `true` for random. */
|
|
79
|
+
extraEntropy: boolean | Uint8Array
|
|
80
|
+
/** Whether to hash the payload before signing. */
|
|
81
|
+
prehash: boolean
|
|
82
|
+
},
|
|
83
|
+
) => Uint8Array)
|
|
84
|
+
| undefined
|
|
85
|
+
/** Verifies a 64-byte compact (`r || s`) signature. */
|
|
86
|
+
verify?:
|
|
87
|
+
| ((
|
|
88
|
+
signature: Uint8Array,
|
|
89
|
+
payload: Uint8Array,
|
|
90
|
+
publicKey: Uint8Array,
|
|
91
|
+
options: {
|
|
92
|
+
/** Whether to hash the payload before verifying. */
|
|
93
|
+
prehash: boolean
|
|
94
|
+
},
|
|
95
|
+
) => boolean)
|
|
96
|
+
| undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Edwards-curve signatures (Ed25519).
|
|
101
|
+
*
|
|
102
|
+
* @internal
|
|
103
|
+
*/
|
|
104
|
+
export type Eddsa = {
|
|
105
|
+
/** Derives a 32-byte public key. */
|
|
106
|
+
getPublicKey?: ((privateKey: Uint8Array) => Uint8Array) | undefined
|
|
107
|
+
/** Generates a random private key. */
|
|
108
|
+
randomSecretKey?: (() => Uint8Array) | undefined
|
|
109
|
+
/** Signs a payload, returning a 64-byte signature. */
|
|
110
|
+
sign?:
|
|
111
|
+
| ((payload: Uint8Array, privateKey: Uint8Array) => Uint8Array)
|
|
112
|
+
| undefined
|
|
113
|
+
/** Converts an Ed25519 public key to its X25519 (Montgomery) form. */
|
|
114
|
+
toMontgomery?: ((publicKey: Uint8Array) => Uint8Array) | undefined
|
|
115
|
+
/** Converts an Ed25519 private key to its X25519 (Montgomery) form. */
|
|
116
|
+
toMontgomerySecret?: ((privateKey: Uint8Array) => Uint8Array) | undefined
|
|
117
|
+
/** Verifies a 64-byte signature. */
|
|
118
|
+
verify?:
|
|
119
|
+
| ((
|
|
120
|
+
signature: Uint8Array,
|
|
121
|
+
payload: Uint8Array,
|
|
122
|
+
publicKey: Uint8Array,
|
|
123
|
+
) => boolean)
|
|
124
|
+
| undefined
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Montgomery-curve key agreement (X25519).
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
export type Ecdh = {
|
|
133
|
+
/** Derives a 32-byte public key. */
|
|
134
|
+
getPublicKey?: ((privateKey: Uint8Array) => Uint8Array) | undefined
|
|
135
|
+
/** Computes a 32-byte shared secret. */
|
|
136
|
+
getSharedSecret?:
|
|
137
|
+
| ((privateKey: Uint8Array, publicKey: Uint8Array) => Uint8Array)
|
|
138
|
+
| undefined
|
|
139
|
+
/** Generates a random private key. */
|
|
140
|
+
randomSecretKey?: (() => Uint8Array) | undefined
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Key derivation and symmetric primitives used by keystores.
|
|
145
|
+
*
|
|
146
|
+
* The `*Async` functions resolve independently of their synchronous twins --
|
|
147
|
+
* an engine that supplies only `scrypt` leaves `scryptAsync` on the default
|
|
148
|
+
* implementation, because wrapping a long synchronous call in a promise is
|
|
149
|
+
* worse than a genuinely yielding one.
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export type Keystore = {
|
|
154
|
+
/** AES-CTR decryption (key length selects AES-128/192/256). */
|
|
155
|
+
aesCtrDecrypt?:
|
|
156
|
+
| ((key: Uint8Array, iv: Uint8Array, data: Uint8Array) => Uint8Array)
|
|
157
|
+
| undefined
|
|
158
|
+
/** AES-CTR encryption (key length selects AES-128/192/256). */
|
|
159
|
+
aesCtrEncrypt?:
|
|
160
|
+
| ((key: Uint8Array, iv: Uint8Array, data: Uint8Array) => Uint8Array)
|
|
161
|
+
| undefined
|
|
162
|
+
/** PBKDF2-HMAC-SHA256. */
|
|
163
|
+
pbkdf2Sha256?:
|
|
164
|
+
| ((
|
|
165
|
+
password: Uint8Array,
|
|
166
|
+
salt: Uint8Array,
|
|
167
|
+
options: { c: number; dkLen: number },
|
|
168
|
+
) => Uint8Array)
|
|
169
|
+
| undefined
|
|
170
|
+
/** Asynchronous PBKDF2-HMAC-SHA256. */
|
|
171
|
+
pbkdf2Sha256Async?:
|
|
172
|
+
| ((
|
|
173
|
+
password: Uint8Array,
|
|
174
|
+
salt: Uint8Array,
|
|
175
|
+
options: { c: number; dkLen: number },
|
|
176
|
+
) => Promise<Uint8Array>)
|
|
177
|
+
| undefined
|
|
178
|
+
/** scrypt. */
|
|
179
|
+
scrypt?:
|
|
180
|
+
| ((
|
|
181
|
+
password: Uint8Array,
|
|
182
|
+
salt: Uint8Array,
|
|
183
|
+
options: { N: number; dkLen: number; p: number; r: number },
|
|
184
|
+
) => Uint8Array)
|
|
185
|
+
| undefined
|
|
186
|
+
/** Asynchronous scrypt. */
|
|
187
|
+
scryptAsync?:
|
|
188
|
+
| ((
|
|
189
|
+
password: Uint8Array,
|
|
190
|
+
salt: Uint8Array,
|
|
191
|
+
options: { N: number; dkLen: number; p: number; r: number },
|
|
192
|
+
) => Promise<Uint8Array>)
|
|
193
|
+
| undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* BIP-39 seed derivation (PBKDF2-HMAC-SHA512, 2048 iterations).
|
|
198
|
+
*
|
|
199
|
+
* Mnemonic generation, validation, and the wordlists themselves are plain data
|
|
200
|
+
* and string handling, so they are not part of the engine boundary.
|
|
201
|
+
*
|
|
202
|
+
* @internal
|
|
203
|
+
*/
|
|
204
|
+
export type Mnemonic = {
|
|
205
|
+
/** Derives a 64-byte seed from a mnemonic phrase and optional passphrase. */
|
|
206
|
+
toSeed?: ((mnemonic: string, passphrase?: string) => Uint8Array) | undefined
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* BLS12-381 operations.
|
|
211
|
+
*
|
|
212
|
+
* Points cross this boundary in their compressed serialized form (48 bytes for
|
|
213
|
+
* G1, 96 for G2), never as projective coordinate triples -- an engine backed by
|
|
214
|
+
* a native library cannot be expected to reproduce another library's internal
|
|
215
|
+
* point representation.
|
|
216
|
+
*
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
export type Bls = {
|
|
220
|
+
/** Aggregates compressed points of the same group. */
|
|
221
|
+
aggregate?:
|
|
222
|
+
| ((points: readonly Uint8Array[], group: 'G1' | 'G2') => Uint8Array)
|
|
223
|
+
| undefined
|
|
224
|
+
/** Derives a compressed public key in the given group. */
|
|
225
|
+
getPublicKey?:
|
|
226
|
+
| ((privateKey: Uint8Array, group: 'G1' | 'G2') => Uint8Array)
|
|
227
|
+
| undefined
|
|
228
|
+
/** Generates a random private key. */
|
|
229
|
+
randomSecretKey?: (() => Uint8Array) | undefined
|
|
230
|
+
/** Signs a payload, returning a compressed point in the opposite group. */
|
|
231
|
+
sign?:
|
|
232
|
+
| ((
|
|
233
|
+
payload: Uint8Array,
|
|
234
|
+
privateKey: Uint8Array,
|
|
235
|
+
options: {
|
|
236
|
+
/** Domain separation tag for hash-to-curve. */
|
|
237
|
+
dst?: Uint8Array | undefined
|
|
238
|
+
/** Group of the signature. */
|
|
239
|
+
group: 'G1' | 'G2'
|
|
240
|
+
},
|
|
241
|
+
) => Uint8Array)
|
|
242
|
+
| undefined
|
|
243
|
+
/** Verifies a compressed signature against a compressed public key. */
|
|
244
|
+
verify?:
|
|
245
|
+
| ((
|
|
246
|
+
signature: Uint8Array,
|
|
247
|
+
payload: Uint8Array,
|
|
248
|
+
publicKey: Uint8Array,
|
|
249
|
+
options: {
|
|
250
|
+
/** Domain separation tag for hash-to-curve. */
|
|
251
|
+
dst?: Uint8Array | undefined
|
|
252
|
+
/** Group of the signature. */
|
|
253
|
+
signatureGroup: 'G1' | 'G2'
|
|
254
|
+
},
|
|
255
|
+
) => boolean)
|
|
256
|
+
| undefined
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Root engine type. Slots and functions are optional; omissions preserve
|
|
261
|
+
* earlier overrides, or use Ox's default when none exists.
|
|
262
|
+
*
|
|
263
|
+
* @internal
|
|
264
|
+
*/
|
|
265
|
+
export type Engine = {
|
|
266
|
+
/** Implementation for {@link ox#Bls}. */
|
|
267
|
+
Bls?: Bls | undefined
|
|
268
|
+
/** Implementation for {@link ox#Ed25519}. */
|
|
269
|
+
Ed25519?: Eddsa | undefined
|
|
270
|
+
/** Implementation for {@link ox#Hash}. */
|
|
271
|
+
Hash?: Hash | undefined
|
|
272
|
+
/** Implementation for {@link ox#Keystore}. */
|
|
273
|
+
Keystore?: Keystore | undefined
|
|
274
|
+
/** Implementation for {@link ox#Mnemonic}. */
|
|
275
|
+
Mnemonic?: Mnemonic | undefined
|
|
276
|
+
/** Implementation for {@link ox#P256}. */
|
|
277
|
+
P256?: Ecdsa | undefined
|
|
278
|
+
/** Implementation for {@link ox#Secp256k1}. */
|
|
279
|
+
Secp256k1?: Ecdsa | undefined
|
|
280
|
+
/** Implementation for {@link ox#X25519}. */
|
|
281
|
+
X25519?: Ecdh | undefined
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* A slot with every function present.
|
|
286
|
+
*
|
|
287
|
+
* Slots are optional all the way down so that an engine can fill in as little
|
|
288
|
+
* as it likes. ox's own default implementations are held to the opposite
|
|
289
|
+
* standard: they back every function ox routes, so they are declared against
|
|
290
|
+
* this instead, and a default that goes missing or drifts from the contract
|
|
291
|
+
* fails to compile.
|
|
292
|
+
*
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
295
|
+
export type Complete<slot> = { [key in keyof slot]-?: NonNullable<slot[key]> }
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Recognized slot names, in the order they appear on {@link Engine}.
|
|
299
|
+
*
|
|
300
|
+
* @internal
|
|
301
|
+
*/
|
|
302
|
+
export const slots = [
|
|
303
|
+
'Bls',
|
|
304
|
+
'Ed25519',
|
|
305
|
+
'Hash',
|
|
306
|
+
'Keystore',
|
|
307
|
+
'Mnemonic',
|
|
308
|
+
'P256',
|
|
309
|
+
'Secp256k1',
|
|
310
|
+
'X25519',
|
|
311
|
+
] as const satisfies readonly (keyof Engine)[]
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Recognized primitive names, per slot.
|
|
315
|
+
*
|
|
316
|
+
* An override stored under a name no resolver reads is invisible: the default
|
|
317
|
+
* keeps running and nothing is reported, so a typo looks exactly like success.
|
|
318
|
+
* That is worst for the callers who can least afford it -- someone installing a
|
|
319
|
+
* policy-constrained or hardware-backed implementation silently gets ox's
|
|
320
|
+
* default instead. {@link Engine.set} checks against this to make it loud.
|
|
321
|
+
*
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
324
|
+
export const primitives = {
|
|
325
|
+
Bls: ['aggregate', 'getPublicKey', 'randomSecretKey', 'sign', 'verify'],
|
|
326
|
+
Ed25519: [
|
|
327
|
+
'getPublicKey',
|
|
328
|
+
'randomSecretKey',
|
|
329
|
+
'sign',
|
|
330
|
+
'toMontgomery',
|
|
331
|
+
'toMontgomerySecret',
|
|
332
|
+
'verify',
|
|
333
|
+
],
|
|
334
|
+
Hash: ['blake3', 'hmacSha256', 'keccak256', 'ripemd160', 'sha256'],
|
|
335
|
+
Keystore: [
|
|
336
|
+
'aesCtrDecrypt',
|
|
337
|
+
'aesCtrEncrypt',
|
|
338
|
+
'pbkdf2Sha256',
|
|
339
|
+
'pbkdf2Sha256Async',
|
|
340
|
+
'scrypt',
|
|
341
|
+
'scryptAsync',
|
|
342
|
+
],
|
|
343
|
+
Mnemonic: ['toSeed'],
|
|
344
|
+
P256: [
|
|
345
|
+
'getPublicKey',
|
|
346
|
+
'getSharedSecret',
|
|
347
|
+
'recoverPublicKey',
|
|
348
|
+
'randomSecretKey',
|
|
349
|
+
'sign',
|
|
350
|
+
'verify',
|
|
351
|
+
],
|
|
352
|
+
Secp256k1: [
|
|
353
|
+
'getPublicKey',
|
|
354
|
+
'getSharedSecret',
|
|
355
|
+
'recoverPublicKey',
|
|
356
|
+
'randomSecretKey',
|
|
357
|
+
'sign',
|
|
358
|
+
'verify',
|
|
359
|
+
],
|
|
360
|
+
X25519: ['getPublicKey', 'getSharedSecret', 'randomSecretKey'],
|
|
361
|
+
} as const satisfies {
|
|
362
|
+
[key in keyof Engine]-?: readonly (keyof Complete<NonNullable<Engine[key]>>)[]
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The installed overrides. Empty by default -- a bare object literal, so
|
|
367
|
+
* bundlers treat it as side-effect free.
|
|
368
|
+
*
|
|
369
|
+
* @internal
|
|
370
|
+
*/
|
|
371
|
+
export const overrides: Engine = {}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Merges overrides into the registry, one level deep. A slot set to `undefined`
|
|
375
|
+
* is removed.
|
|
376
|
+
*
|
|
377
|
+
* @internal
|
|
378
|
+
*/
|
|
379
|
+
export function merge(value: Engine) {
|
|
380
|
+
for (const key of Object.keys(value) as (keyof Engine)[]) {
|
|
381
|
+
const slot = value[key]
|
|
382
|
+
if (slot === undefined) {
|
|
383
|
+
delete overrides[key]
|
|
384
|
+
continue
|
|
385
|
+
}
|
|
386
|
+
// A member set to `undefined` clears that primitive. Spreading would keep
|
|
387
|
+
// it as an own property: calls still fall through to the default, but
|
|
388
|
+
// `get` would report an override that is not there.
|
|
389
|
+
const merged: Record<string, unknown> = { ...overrides[key] }
|
|
390
|
+
for (const [name, fn] of Object.entries(slot)) {
|
|
391
|
+
if (fn === undefined) delete merged[name]
|
|
392
|
+
else merged[name] = fn
|
|
393
|
+
}
|
|
394
|
+
// An empty slot stays: `set({ Hash: {} })` is a no-op a caller wrote
|
|
395
|
+
// deliberately, and `get` has always reported it.
|
|
396
|
+
overrides[key] = merged as never
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Removes one slot, or every slot when `slot` is omitted.
|
|
402
|
+
*
|
|
403
|
+
* @internal
|
|
404
|
+
*/
|
|
405
|
+
export function reset(slot?: keyof Engine) {
|
|
406
|
+
if (slot) delete overrides[slot]
|
|
407
|
+
else
|
|
408
|
+
for (const key of Object.keys(overrides) as (keyof Engine)[])
|
|
409
|
+
delete overrides[key]
|
|
410
|
+
}
|