utils: fix hexToNumber, improve validateObject

This commit is contained in:
Paul Miller 2023-01-30 05:07:53 +00:00
parent 4780850748
commit e14af67254
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B

@ -35,7 +35,7 @@ export function numberToHexUnpadded(num: number | bigint): string {
export function hexToNumber(hex: string): bigint { export function hexToNumber(hex: string): bigint {
if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex); if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex);
// Big Endian // Big Endian
return BigInt(`0x${hex}`); return BigInt(hex === '' ? '0' : `0x${hex}`);
} }
// Caching slows it down 2-3x // Caching slows it down 2-3x
@ -114,31 +114,47 @@ export const bitSet = (n: bigint, pos: number, value: boolean) =>
// Not using ** operator with bigints for old engines. // Not using ** operator with bigints for old engines.
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n; export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
type ValMap = Record<string, string>; const validatorFns = {
export function validateObject(object: object, validators: ValMap, optValidators: ValMap = {}) { bigint: (val: any) => typeof val === 'bigint',
const validatorFns: Record<string, (val: any) => boolean> = { function: (val: any) => typeof val === 'function',
bigint: (val) => typeof val === 'bigint', boolean: (val: any) => typeof val === 'boolean',
function: (val) => typeof val === 'function', string: (val: any) => typeof val === 'string',
boolean: (val) => typeof val === 'boolean', isSafeInteger: (val: any) => Number.isSafeInteger(val),
string: (val) => typeof val === 'string', array: (val: any) => Array.isArray(val),
isSafeInteger: (val) => Number.isSafeInteger(val), field: (val: any, object: any) => (object as any).Fp.isValid(val),
array: (val) => Array.isArray(val), hash: (val: any) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
field: (val) => (object as any).Fp.isValid(val), } as const;
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), type Validator = keyof typeof validatorFns;
}; type ValMap<T extends Record<string, any>> = { [K in keyof T]?: Validator };
// type Key = keyof typeof validators; // type Record<K extends string | number | symbol, T> = { [P in K]: T; }
const checkField = (fieldName: string, type: string, isOptional: boolean) => {
export function validateObject<T extends Record<string, any>>(
object: T,
validators: ValMap<T>,
optValidators: ValMap<T> = {}
) {
const checkField = (fieldName: keyof T, type: Validator, isOptional: boolean) => {
const checkVal = validatorFns[type]; const checkVal = validatorFns[type];
if (typeof checkVal !== 'function') if (typeof checkVal !== 'function')
throw new Error(`Invalid validator "${type}", expected function`); throw new Error(`Invalid validator "${type}", expected function`);
const val = object[fieldName as keyof typeof object]; const val = object[fieldName as keyof typeof object];
if (isOptional && val === undefined) return; if (isOptional && val === undefined) return;
if (!checkVal(val)) { if (!checkVal(val, object)) {
throw new Error(`Invalid param ${fieldName}=${val} (${typeof val}), expected ${type}`); throw new Error(
`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`
);
} }
}; };
for (let [fieldName, type] of Object.entries(validators)) checkField(fieldName, type, false); for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type!, false);
for (let [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type, true); for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type!, true);
return object; return object;
} }
// validate type tests
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
// // Should fail type-check
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });