utils.ts
1 import type { CodecValue } from "@massmarket/utils/codec"; 2 export class BaseClass { 3 static isBaseClass(value: unknown): value is BaseClass { 4 return ( 5 value instanceof BaseClass || 6 (value !== null && typeof value === "object" && "asCBORMap" in value && 7 typeof value.asCBORMap === "function") 8 ); 9 } 10 11 asCBORMap(): CodecValue { 12 const map = new Map(); 13 for (const [key, value] of Object.entries(this)) { 14 if ( 15 ["string", "number", "boolean", "bigint"].includes(typeof value) || 16 value instanceof Uint8Array 17 ) { 18 map.set(key, value); 19 } else if (value instanceof Date) { 20 map.set(key, value); 21 } else if (Array.isArray(value)) { 22 const newArray = []; 23 for (const v of value) { 24 if (BaseClass.isBaseClass(v)) { 25 newArray.push(v.asCBORMap()); 26 } else { 27 newArray.push(v); 28 } 29 } 30 map.set(key, newArray); 31 } else if (value instanceof Map) { 32 const newMap = new Map(); 33 for (const [k, v] of value.entries()) { 34 if (BaseClass.isBaseClass(v)) { 35 newMap.set(k, v.asCBORMap()); 36 } else { 37 newMap.set(k, v); 38 } 39 } 40 map.set(key, newMap); 41 } else if (BaseClass.isBaseClass(value)) { 42 map.set(key, value.asCBORMap()); 43 } else if (value === undefined) { 44 // ignore 45 } else { 46 throw new Error( 47 `Unknown value on ${key}. Type: ${typeof value}`, 48 ); 49 } 50 } 51 return map; 52 } 53 } 54 55 // Helper functions for type validation 56 export function ensureNumber(value: unknown, fieldName: string): number { 57 if (typeof value !== "number" || isNaN(value)) { 58 throw new TypeError( 59 `Expected ${fieldName} to be a number, got ${typeof value}`, 60 ); 61 } 62 return value; 63 } 64 65 // Helper functions for type validation 66 export function ensureBigInt(value: unknown, fieldName: string): bigint { 67 if (typeof value !== "bigint") { 68 throw new TypeError( 69 `Expected ${fieldName} to be a bigint, got ${typeof value}`, 70 ); 71 } 72 return value; 73 } 74 75 // Helper returns number or bigint 76 export function ensureSomeNumberAsBigInt( 77 value: unknown, 78 fieldName: string, 79 ): bigint | number { 80 if (typeof value === "bigint") { 81 return value; 82 } 83 const num = ensureNumber(value, fieldName); 84 // TODO: i'd prefer this but when going to asCBORMap() it needs to convert to smallest possible value, which is annoying.. 85 // return BigInt(num); 86 return num; 87 } 88 89 export function ensureString(value: unknown, fieldName: string): string { 90 if (typeof value !== "string") { 91 throw new TypeError( 92 `Expected ${fieldName} to be a string, got ${typeof value}`, 93 ); 94 } 95 return value; 96 } 97 98 export function ensureBoolean(value: unknown, fieldName: string): boolean { 99 if (typeof value !== "boolean") { 100 throw new TypeError( 101 `Expected ${fieldName} to be a boolean, got ${typeof value}`, 102 ); 103 } 104 return value; 105 } 106 107 export function ensureStringArray(value: unknown, fieldName: string): string[] { 108 if ( 109 !Array.isArray(value) || !value.every((item) => typeof item === "string") 110 ) { 111 throw new TypeError(`Expected ${fieldName} to be an array of strings`); 112 } 113 return value; 114 } 115 116 export function ensureMap<K, V>(value: unknown, fieldName: string): Map<K, V> { 117 if (!(value instanceof Map)) { 118 throw new TypeError( 119 `Expected ${fieldName} to be a Map, got ${typeof value}`, 120 ); 121 } 122 return value as Map<K, V>; 123 } 124 125 // export function ensureMap(value: unknown, fieldName: string): Map<string, unknown> { 126 // if (!(value instanceof Map)) { 127 // throw new TypeError(`Expected ${fieldName} to be a Map, got ${typeof value}`); 128 // } 129 // return value; 130 // } 131 132 export function ensureUint8Array( 133 value: unknown, 134 fieldName: string, 135 length?: number, 136 ): Uint8Array { 137 if (!(value instanceof Uint8Array)) { 138 throw new TypeError( 139 `Expected ${fieldName} to be a Uint8Array, got ${typeof value}`, 140 ); 141 } 142 if (length !== undefined && value.length !== length) { 143 throw new TypeError( 144 `Expected ${fieldName} to be a Uint8Array of length ${length}, got ${value.length}`, 145 ); 146 } 147 return value; 148 } 149 150 export function ensureDate(value: unknown, fieldName: string): Date { 151 if (!(value instanceof Date)) { 152 throw new TypeError( 153 `Expected ${fieldName} to be a Date, got ${typeof value}`, 154 ); 155 } 156 return value; 157 }