@noble/curves 0.6.0 → 0.6.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.
@@ -1,32 +1,27 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
3
3
  import { mod } from './modular.js';
4
- import { bytesToHex, bytesToNumberLE, concatBytes, ensureBytes, numberToBytesLE, } from './utils.js';
5
- import { wNAF, validateAbsOpts, } from './curve.js';
4
+ import * as ut from './utils.js';
5
+ import { ensureBytes } from './utils.js';
6
+ import { wNAF, validateBasic } from './curve.js';
6
7
  // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
7
8
  const _0n = BigInt(0);
8
9
  const _1n = BigInt(1);
9
10
  const _2n = BigInt(2);
10
11
  const _8n = BigInt(8);
11
12
  function validateOpts(curve) {
12
- const opts = validateAbsOpts(curve);
13
- if (typeof opts.hash !== 'function')
14
- throw new Error('Invalid hash function');
15
- for (const i of ['a', 'd']) {
16
- const val = opts[i];
17
- if (typeof val !== 'bigint')
18
- throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
19
- }
20
- for (const fn of ['randomBytes']) {
21
- if (typeof opts[fn] !== 'function')
22
- throw new Error(`Invalid ${fn} function`);
23
- }
24
- for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
25
- if (opts[fn] === undefined)
26
- continue; // Optional
27
- if (typeof opts[fn] !== 'function')
28
- throw new Error(`Invalid ${fn} function`);
29
- }
13
+ const opts = validateBasic(curve);
14
+ ut.validateObject(curve, {
15
+ hash: 'function',
16
+ a: 'bigint',
17
+ d: 'bigint',
18
+ randomBytes: 'function',
19
+ }, {
20
+ adjustScalarBytes: 'function',
21
+ domain: 'function',
22
+ uvRatio: 'function',
23
+ mapToCurve: 'function',
24
+ });
30
25
  // Set defaults
31
26
  return Object.freeze({ ...opts });
32
27
  }
@@ -111,7 +106,30 @@ export function twistedEdwards(curveDef) {
111
106
  this._WINDOW_SIZE = windowSize;
112
107
  pointPrecomputes.delete(this);
113
108
  }
114
- assertValidity() { }
109
+ // Not required for fromHex(), which always creates valid points.
110
+ // Could be useful for fromAffine().
111
+ assertValidity() {
112
+ const { a, d } = CURVE;
113
+ if (this.is0())
114
+ throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
115
+ // Equation in affine coordinates: ax² + y² = 1 + dx²y²
116
+ // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
117
+ const { ex: X, ey: Y, ez: Z, et: T } = this;
118
+ const X2 = modP(X * X); // X²
119
+ const Y2 = modP(Y * Y); // Y²
120
+ const Z2 = modP(Z * Z); // Z²
121
+ const Z4 = modP(Z2 * Z2); // Z⁴
122
+ const aX2 = modP(X2 * a); // aX²
123
+ const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
124
+ const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
125
+ if (left !== right)
126
+ throw new Error('bad point: equation left != right (1)');
127
+ // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
128
+ const XY = modP(X * Y);
129
+ const ZT = modP(Z * T);
130
+ if (XY !== ZT)
131
+ throw new Error('bad point: equation left != right (2)');
132
+ }
115
133
  // Compare one point to another.
116
134
  equals(other) {
117
135
  isPoint(other);
@@ -261,7 +279,7 @@ export function twistedEdwards(curveDef) {
261
279
  const normed = hex.slice(); // copy again, we'll manipulate it
262
280
  const lastByte = hex[len - 1]; // select last byte
263
281
  normed[len - 1] = lastByte & ~0x80; // clear last bit
264
- const y = bytesToNumberLE(normed);
282
+ const y = ut.bytesToNumberLE(normed);
265
283
  if (y === _0n) {
266
284
  // y=0 is allowed
267
285
  }
@@ -291,12 +309,12 @@ export function twistedEdwards(curveDef) {
291
309
  }
292
310
  toRawBytes() {
293
311
  const { x, y } = this.toAffine();
294
- const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
312
+ const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
295
313
  bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
296
314
  return bytes; // and use the last byte to encode sign of x
297
315
  }
298
316
  toHex() {
299
- return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
317
+ return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
300
318
  }
301
319
  }
302
320
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
@@ -308,7 +326,7 @@ export function twistedEdwards(curveDef) {
308
326
  }
309
327
  // Little-endian SHA512 with modulo n
310
328
  function modN_LE(hash) {
311
- return modN(bytesToNumberLE(hash));
329
+ return modN(ut.bytesToNumberLE(hash));
312
330
  }
313
331
  function isHex(item, err) {
314
332
  if (typeof item !== 'string' && !(item instanceof Uint8Array))
@@ -334,7 +352,7 @@ export function twistedEdwards(curveDef) {
334
352
  }
335
353
  // int('LE', SHA512(dom2(F, C) || msgs)) mod N
336
354
  function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
337
- const msg = concatBytes(...msgs);
355
+ const msg = ut.concatBytes(...msgs);
338
356
  return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
339
357
  }
340
358
  /** Signs message with privateKey. RFC8032 5.1.6 */
@@ -349,7 +367,7 @@ export function twistedEdwards(curveDef) {
349
367
  const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
350
368
  const s = modN(r + k * scalar); // S = (r + k * s) mod L
351
369
  assertGE0(s); // 0 <= s < l
352
- const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
370
+ const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
353
371
  return ensureBytes(res, nByteLength * 2); // 64-byte signature
354
372
  }
355
373
  function verify(sig, msg, publicKey, context) {
@@ -362,7 +380,7 @@ export function twistedEdwards(curveDef) {
362
380
  msg = preHash(msg); // for ed25519ph, etc
363
381
  const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
364
382
  const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
365
- const s = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
383
+ const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
366
384
  const SB = G.multiplyUnsafe(s);
367
385
  const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
368
386
  const RkA = R.add(A.multiplyUnsafe(k));
@@ -16,7 +16,7 @@ export function validateOpts(opts) {
16
16
  }
17
17
  export function stringToBytes(str) {
18
18
  if (typeof str !== 'string') {
19
- throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
19
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
20
20
  }
21
21
  return new TextEncoder().encode(str);
22
22
  }
@@ -1,6 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  // Utilities for modular arithmetics and finite fields
3
- import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, } from './utils.js';
3
+ import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, validateObject, } from './utils.js';
4
4
  // prettier-ignore
5
5
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
6
6
  // prettier-ignore
@@ -34,7 +34,6 @@ export function pow(num, power, modulo) {
34
34
  return res;
35
35
  }
36
36
  // Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
37
- // TODO: Fp version?
38
37
  export function pow2(x, power, modulo) {
39
38
  let res = x;
40
39
  while (power-- > _0n) {
@@ -193,18 +192,17 @@ const FIELD_FIELDS = [
193
192
  'addN', 'subN', 'mulN', 'sqrN'
194
193
  ];
195
194
  export function validateField(field) {
196
- for (const i of ['ORDER', 'MASK']) {
197
- if (typeof field[i] !== 'bigint')
198
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
199
- }
200
- for (const i of ['BYTES', 'BITS']) {
201
- if (typeof field[i] !== 'number')
202
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
203
- }
204
- for (const i of FIELD_FIELDS) {
205
- if (typeof field[i] !== 'function')
206
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
207
- }
195
+ const initial = {
196
+ ORDER: 'bigint',
197
+ MASK: 'bigint',
198
+ BYTES: 'isSafeInteger',
199
+ BITS: 'isSafeInteger',
200
+ };
201
+ const opts = FIELD_FIELDS.reduce((map, val) => {
202
+ map[val] = 'function';
203
+ return map;
204
+ }, initial);
205
+ return validateObject(field, opts);
208
206
  }
209
207
  // Generic field functions
210
208
  export function FpPow(f, num, power) {
@@ -1,31 +1,19 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  import { mod, pow } from './modular.js';
3
- import { ensureBytes, numberToBytesLE, bytesToNumberLE } from './utils.js';
3
+ import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
4
4
  const _0n = BigInt(0);
5
5
  const _1n = BigInt(1);
6
6
  function validateOpts(curve) {
7
- for (const i of ['a24']) {
8
- if (typeof curve[i] !== 'bigint')
9
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
10
- }
11
- for (const i of ['montgomeryBits', 'nByteLength']) {
12
- if (curve[i] === undefined)
13
- continue; // Optional
14
- if (!Number.isSafeInteger(curve[i]))
15
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
16
- }
17
- for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
18
- if (curve[fn] === undefined)
19
- continue; // Optional
20
- if (typeof curve[fn] !== 'function')
21
- throw new Error(`Invalid ${fn} function`);
22
- }
23
- for (const i of ['Gu']) {
24
- if (curve[i] === undefined)
25
- continue; // Optional
26
- if (typeof curve[i] !== 'string')
27
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
28
- }
7
+ validateObject(curve, {
8
+ a24: 'bigint',
9
+ }, {
10
+ montgomeryBits: 'isSafeInteger',
11
+ nByteLength: 'isSafeInteger',
12
+ adjustScalarBytes: 'function',
13
+ domain: 'function',
14
+ powPminus2: 'function',
15
+ Gu: 'string',
16
+ });
29
17
  // Set defaults
30
18
  return Object.freeze({ ...curve });
31
19
  }
@@ -40,31 +28,7 @@ export function montgomery(curveDef) {
40
28
  const fieldLen = CURVE.nByteLength;
41
29
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
42
30
  const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P));
43
- /**
44
- * Checks for num to be in range:
45
- * For strict == true: `0 < num < max`.
46
- * For strict == false: `0 <= num < max`.
47
- * Converts non-float safe numbers to bigints.
48
- */
49
- function normalizeScalar(num, max, strict = true) {
50
- if (!max)
51
- throw new TypeError('Specify max value');
52
- if (typeof num === 'number' && Number.isSafeInteger(num))
53
- num = BigInt(num);
54
- if (typeof num === 'bigint' && num < max) {
55
- if (strict) {
56
- if (_0n < num)
57
- return num;
58
- }
59
- else {
60
- if (_0n <= num)
61
- return num;
62
- }
63
- }
64
- throw new TypeError('Expected valid scalar: 0 < scalar < max');
65
- }
66
- // cswap from RFC7748
67
- // NOTE: cswap is not from RFC7748!
31
+ // cswap from RFC7748. But it is not from RFC7748!
68
32
  /*
69
33
  cswap(swap, x_2, x_3):
70
34
  dummy = mask(swap) AND (x_2 XOR x_3)
@@ -80,6 +44,11 @@ export function montgomery(curveDef) {
80
44
  x_3 = modP(x_3 + dummy);
81
45
  return [x_2, x_3];
82
46
  }
47
+ function assertFieldElement(n) {
48
+ if (typeof n === 'bigint' && _0n <= n && n < P)
49
+ return n;
50
+ throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
51
+ }
83
52
  // x25519 from 4
84
53
  /**
85
54
  *
@@ -88,11 +57,10 @@ export function montgomery(curveDef) {
88
57
  * @returns new Point on Montgomery curve
89
58
  */
90
59
  function montgomeryLadder(pointU, scalar) {
91
- const { P } = CURVE;
92
- const u = normalizeScalar(pointU, P);
60
+ const u = assertFieldElement(pointU);
93
61
  // Section 5: Implementations MUST accept non-canonical values and process them as
94
62
  // if they had been reduced modulo the field prime.
95
- const k = normalizeScalar(scalar, P);
63
+ const k = assertFieldElement(scalar);
96
64
  // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
97
65
  const a24 = CURVE.a24;
98
66
  const x_1 = u;
@@ -145,12 +113,14 @@ export function montgomery(curveDef) {
145
113
  return numberToBytesLE(modP(u), montgomeryBytes);
146
114
  }
147
115
  function decodeUCoordinate(uEnc) {
148
- const u = ensureBytes(uEnc, montgomeryBytes);
149
116
  // Section 5: When receiving such an array, implementations of X25519
150
117
  // MUST mask the most significant bit in the final byte.
151
118
  // This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
152
119
  // fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
153
- u[fieldLen - 1] &= 127; // 0b0111_1111
120
+ const u = ensureBytes(uEnc, montgomeryBytes);
121
+ // u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
122
+ if (fieldLen === montgomeryBytes)
123
+ u[fieldLen - 1] &= 127; // 0b0111_1111
154
124
  return bytesToNumberLE(u);
155
125
  }
156
126
  function decodeScalar(n) {
@@ -159,13 +129,6 @@ export function montgomery(curveDef) {
159
129
  throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
160
130
  return bytesToNumberLE(adjustScalarBytes(bytes));
161
131
  }
162
- /**
163
- * Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
164
- * We can get 'y' coordinate from 'u',
165
- * but Point.fromHex also wants 'x' coordinate oddity flag,
166
- * and we cannot get 'x' without knowing 'v'.
167
- * Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
168
- */
169
132
  function scalarMult(scalar, u) {
170
133
  const pointU = decodeUCoordinate(u);
171
134
  const _scalar = decodeScalar(scalar);
@@ -176,12 +139,7 @@ export function montgomery(curveDef) {
176
139
  throw new Error('Invalid private or public key received');
177
140
  return encodeUCoordinate(pu);
178
141
  }
179
- /**
180
- * Computes public key from private.
181
- * Executes scalar multiplication of curve's base point by scalar.
182
- * @param scalar private key
183
- * @returns new public key
184
- */
142
+ // Computes public key from private. By doing scalar multiplication of base point.
185
143
  function scalarMultBase(scalar) {
186
144
  return scalarMult(scalar, CURVE.Gu);
187
145
  }
@@ -1,6 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  // Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
3
- import { validateField, FpPow } from './modular.js';
3
+ import { FpPow, validateField } from './modular.js';
4
4
  export function validateOpts(opts) {
5
5
  const { Fp } = opts;
6
6
  validateField(Fp);
@@ -6,7 +6,7 @@ const u8a = (a) => a instanceof Uint8Array;
6
6
  const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
7
7
  export function bytesToHex(bytes) {
8
8
  if (!u8a(bytes))
9
- throw new Error('Expected Uint8Array');
9
+ throw new Error('Uint8Array expected');
10
10
  // pre-caching improves the speed 6x
11
11
  let hex = '';
12
12
  for (let i = 0; i < bytes.length; i++) {
@@ -20,23 +20,23 @@ export function numberToHexUnpadded(num) {
20
20
  }
21
21
  export function hexToNumber(hex) {
22
22
  if (typeof hex !== 'string')
23
- throw new Error('hexToNumber: expected string, got ' + typeof hex);
23
+ throw new Error('string expected, got ' + typeof hex);
24
24
  // Big Endian
25
- return BigInt(`0x${hex}`);
25
+ return BigInt(hex === '' ? '0' : `0x${hex}`);
26
26
  }
27
27
  // Caching slows it down 2-3x
28
28
  export function hexToBytes(hex) {
29
29
  if (typeof hex !== 'string')
30
- throw new Error('hexToBytes: expected string, got ' + typeof hex);
30
+ throw new Error('string expected, got ' + typeof hex);
31
31
  if (hex.length % 2)
32
- throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
32
+ throw new Error('hex string is invalid: unpadded ' + hex.length);
33
33
  const array = new Uint8Array(hex.length / 2);
34
34
  for (let i = 0; i < array.length; i++) {
35
35
  const j = i * 2;
36
36
  const hexByte = hex.slice(j, j + 2);
37
37
  const byte = Number.parseInt(hexByte, 16);
38
38
  if (Number.isNaN(byte) || byte < 0)
39
- throw new Error('Invalid byte sequence');
39
+ throw new Error('invalid byte sequence');
40
40
  array[i] = byte;
41
41
  }
42
42
  return array;
@@ -47,18 +47,13 @@ export function bytesToNumberBE(bytes) {
47
47
  }
48
48
  export function bytesToNumberLE(bytes) {
49
49
  if (!u8a(bytes))
50
- throw new Error('Expected Uint8Array');
50
+ throw new Error('Uint8Array expected');
51
51
  return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
52
52
  }
53
53
  export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
54
54
  export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse();
55
55
  // Returns variable number bytes (minimal bigint encoding?)
56
- export const numberToVarBytesBE = (n) => {
57
- let hex = n.toString(16);
58
- if (hex.length & 1)
59
- hex = '0' + hex;
60
- return hexToBytes(hex);
61
- };
56
+ export const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n));
62
57
  export function ensureBytes(hex, expectedLength) {
63
58
  // Uint8Array.from() instead of hash.slice() because node.js Buffer
64
59
  // is instance of Uint8Array, and its slice() creates **mutable** copy
@@ -68,19 +63,16 @@ export function ensureBytes(hex, expectedLength) {
68
63
  return bytes;
69
64
  }
70
65
  // Copies several Uint8Arrays into one.
71
- export function concatBytes(...arrays) {
72
- if (!arrays.every((b) => u8a(b)))
73
- throw new Error('Uint8Array list expected');
74
- if (arrays.length === 1)
75
- return arrays[0];
76
- const length = arrays.reduce((a, arr) => a + arr.length, 0);
77
- const result = new Uint8Array(length);
78
- for (let i = 0, pad = 0; i < arrays.length; i++) {
79
- const arr = arrays[i];
80
- result.set(arr, pad);
81
- pad += arr.length;
82
- }
83
- return result;
66
+ export function concatBytes(...arrs) {
67
+ const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
68
+ let pad = 0; // walk through each item, ensure they have proper type
69
+ arrs.forEach((a) => {
70
+ if (!u8a(a))
71
+ throw new Error('Uint8Array expected');
72
+ r.set(a, pad);
73
+ pad += a.length;
74
+ });
75
+ return r;
84
76
  }
85
77
  export function equalBytes(b1, b2) {
86
78
  // We don't care about timing attacks here
@@ -107,3 +99,40 @@ export const bitSet = (n, pos, value) => n | ((value ? _1n : _0n) << BigInt(pos)
107
99
  // Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
108
100
  // Not using ** operator with bigints for old engines.
109
101
  export const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n;
102
+ const validatorFns = {
103
+ bigint: (val) => typeof val === 'bigint',
104
+ function: (val) => typeof val === 'function',
105
+ boolean: (val) => typeof val === 'boolean',
106
+ string: (val) => typeof val === 'string',
107
+ isSafeInteger: (val) => Number.isSafeInteger(val),
108
+ array: (val) => Array.isArray(val),
109
+ field: (val, object) => object.Fp.isValid(val),
110
+ hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
111
+ };
112
+ // type Record<K extends string | number | symbol, T> = { [P in K]: T; }
113
+ export function validateObject(object, validators, optValidators = {}) {
114
+ const checkField = (fieldName, type, isOptional) => {
115
+ const checkVal = validatorFns[type];
116
+ if (typeof checkVal !== 'function')
117
+ throw new Error(`Invalid validator "${type}", expected function`);
118
+ const val = object[fieldName];
119
+ if (isOptional && val === undefined)
120
+ return;
121
+ if (!checkVal(val, object)) {
122
+ throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`);
123
+ }
124
+ };
125
+ for (const [fieldName, type] of Object.entries(validators))
126
+ checkField(fieldName, type, false);
127
+ for (const [fieldName, type] of Object.entries(optValidators))
128
+ checkField(fieldName, type, true);
129
+ return object;
130
+ }
131
+ // validate type tests
132
+ // const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
133
+ // const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
134
+ // // Should fail type-check
135
+ // const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
136
+ // const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
137
+ // const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
138
+ // const z4 = validateObject(o, { a: 'boolean', z: 'bug' });