UNPKG

@metamask/snaps-utils

Version:
1 lines 23.9 kB
{"version":3,"file":"structs.mjs","sourceRoot":"","sources":["../src/structs.ts"],"names":[],"mappings":"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","sourcesContent":["import { union } from '@metamask/snaps-sdk';\nimport type {\n AnyStruct,\n Assign,\n Failure,\n ObjectSchema,\n ObjectType,\n} from '@metamask/superstruct';\nimport {\n assign,\n is,\n validate,\n type as superstructType,\n Struct,\n StructError,\n create,\n} from '@metamask/superstruct';\nimport type { NonEmptyArray } from '@metamask/utils';\nimport { assert, isObject } from '@metamask/utils';\nimport { bold, green, red } from 'chalk';\n\nimport { indent } from './strings';\n\n/**\n * Infer a struct type, only if it matches the specified type. This is useful\n * for defining types and structs that are related to each other in separate\n * files.\n *\n * @example\n * ```typescript\n * // In file A\n * export type GetFileArgs = {\n * path: string;\n * encoding?: EnumToUnion<AuxiliaryFileEncoding>;\n * };\n *\n * // In file B\n * export const GetFileArgsStruct = object(...);\n *\n * // If the type and struct are in the same file, this will return the type.\n * // Otherwise, it will return `never`.\n * export type GetFileArgs =\n * InferMatching<typeof GetFileArgsStruct, GetFileArgs>;\n * ```\n */\nexport type InferMatching<\n StructType extends Struct<any, any>,\n Type,\n> = StructType['TYPE'] extends Type ? Type : never;\n\n/**\n * Colorize a value with a color function. This is useful for colorizing values\n * in error messages. If colorization is disabled, the original value is\n * returned.\n *\n * @param value - The value to colorize.\n * @param colorFunction - The color function to use.\n * @param enabled - Whether to colorize the value.\n * @returns The colorized value, or the original value if colorization is\n * disabled.\n */\nfunction color(\n value: string,\n colorFunction: (value: string) => string,\n enabled: boolean,\n) {\n if (enabled) {\n return colorFunction(value);\n }\n\n return value;\n}\n\n/**\n * Define a struct, and also define the name of the struct as the given name.\n *\n * This is useful for improving the error messages returned by `superstruct`.\n *\n * @param name - The name of the struct.\n * @param struct - The struct.\n * @returns The struct.\n */\nexport function named<Type, Schema>(\n name: string,\n struct: Struct<Type, Schema>,\n) {\n return new Struct({\n ...struct,\n type: name,\n });\n}\n\nexport class SnapsStructError<Type, Schema> extends StructError {\n constructor(\n struct: Struct<Type, Schema>,\n prefix: string,\n suffix: string,\n failure: StructError,\n failures: () => Generator<Failure>,\n colorize = true,\n ) {\n super(failure, failures);\n\n this.name = 'SnapsStructError';\n this.message = `${prefix}.\\n\\n${getStructErrorMessage(\n struct,\n [...failures()],\n colorize,\n )}${suffix ? `\\n\\n${suffix}` : ''}`;\n }\n}\n\ntype GetErrorOptions<Type, Schema> = {\n struct: Struct<Type, Schema>;\n prefix: string;\n suffix?: string;\n error: StructError;\n colorize?: boolean;\n};\n\n/**\n * Converts an array to a generator function that yields the items in the\n * array.\n *\n * @param array - The array.\n * @returns A generator function.\n * @yields The items in the array.\n */\nexport function* arrayToGenerator<Type>(\n array: Type[],\n): Generator<Type, void, undefined> {\n for (const item of array) {\n yield item;\n }\n}\n\n/**\n * Returns a `SnapsStructError` with the given prefix and suffix.\n *\n * @param options - The options.\n * @param options.struct - The struct that caused the error.\n * @param options.prefix - The prefix to add to the error message.\n * @param options.suffix - The suffix to add to the error message. Defaults to\n * an empty string.\n * @param options.error - The `superstruct` error to wrap.\n * @param options.colorize - Whether to colorize the value. Defaults to `true`.\n * @returns The `SnapsStructError`.\n */\nexport function getError<Type, Schema>({\n struct,\n prefix,\n suffix = '',\n error,\n colorize,\n}: GetErrorOptions<Type, Schema>) {\n return new SnapsStructError(\n struct,\n prefix,\n suffix,\n error,\n () => arrayToGenerator(error.failures()),\n colorize,\n );\n}\n\n/**\n * A wrapper of `superstruct`'s `create` function that throws a\n * `SnapsStructError` instead of a `StructError`. This is useful for improving\n * the error messages returned by `superstruct`.\n *\n * @param value - The value to validate.\n * @param struct - The `superstruct` struct to validate the value against.\n * @param prefix - The prefix to add to the error message.\n * @param suffix - The suffix to add to the error message. Defaults to an empty\n * string.\n * @returns The validated value.\n */\nexport function createFromStruct<Type, Schema>(\n value: unknown,\n struct: Struct<Type, Schema>,\n prefix: string,\n suffix = '',\n) {\n try {\n return create(value, struct);\n } catch (error) {\n if (error instanceof StructError) {\n throw getError({ struct, prefix, suffix, error });\n }\n\n throw error;\n }\n}\n\n/**\n * Get a struct from a failure path.\n *\n * @param struct - The struct.\n * @param path - The failure path.\n * @returns The struct at the failure path.\n */\nexport function getStructFromPath<Type, Schema>(\n struct: Struct<Type, Schema>,\n path: string[],\n) {\n return path.reduce<AnyStruct>((result, key) => {\n if (isObject(struct.schema) && struct.schema[key]) {\n return struct.schema[key] as AnyStruct;\n }\n\n return result;\n }, struct);\n}\n\n/**\n * Get the union struct names from a struct.\n *\n * @param struct - The struct.\n * @param colorize - Whether to colorize the value. Defaults to `true`.\n * @returns The union struct names, or `null` if the struct is not a union\n * struct.\n */\nexport function getUnionStructNames<Type, Schema>(\n struct: Struct<Type, Schema>,\n colorize = true,\n) {\n if (Array.isArray(struct.schema)) {\n return struct.schema.map(({ type }) => color(type, green, colorize));\n }\n\n return null;\n}\n\n/**\n * Get an error prefix from a `superstruct` failure. This is useful for\n * formatting the error message returned by `superstruct`.\n *\n * @param failure - The `superstruct` failure.\n * @param colorize - Whether to colorize the value. Defaults to `true`.\n * @returns The error prefix.\n */\nexport function getStructErrorPrefix(failure: Failure, colorize = true) {\n if (failure.type === 'never' || failure.path.length === 0) {\n return '';\n }\n\n return `At path: ${color(failure.path.join('.'), bold, colorize)} — `;\n}\n\n/**\n * Get a string describing the failure. This is similar to the `message`\n * property of `superstruct`'s `Failure` type, but formats the value in a more\n * readable way.\n *\n * @param struct - The struct that caused the failure.\n * @param failure - The `superstruct` failure.\n * @param colorize - Whether to colorize the value. Defaults to `true`.\n * @returns A string describing the failure.\n */\nexport function getStructFailureMessage<Type, Schema>(\n struct: Struct<Type, Schema>,\n failure: Failure,\n colorize = true,\n) {\n const received = color(JSON.stringify(failure.value), red, colorize);\n const prefix = getStructErrorPrefix(failure, colorize);\n\n if (failure.type === 'union') {\n const childStruct = getStructFromPath(struct, failure.path);\n const unionNames = getUnionStructNames(childStruct, colorize);\n\n if (unionNames) {\n return `${prefix}Expected the value to be one of: ${unionNames.join(\n ', ',\n )}, but received: ${received}.`;\n }\n\n return `${prefix}${failure.message}.`;\n }\n\n if (failure.type === 'literal') {\n // Superstruct's failure does not provide information about which literal\n // value was expected, so we need to parse the message to get the literal.\n const message = failure.message\n .replace(\n /the literal `(.+)`,/u,\n `the value to be \\`${color('$1', green, colorize)}\\`,`,\n )\n .replace(\n /, but received: (.+)/u,\n `, but received: ${color('$1', red, colorize)}`,\n );\n\n return `${prefix}${message}.`;\n }\n\n if (failure.type === 'never') {\n return `Unknown key: ${color(\n failure.path.join('.'),\n bold,\n colorize,\n )}, received: ${received}.`;\n }\n\n if (failure.refinement === 'size') {\n const message = failure.message\n .replace(\n /length between `(\\d+)` and `(\\d+)`/u,\n `length between ${color('$1', green, colorize)} and ${color(\n '$2',\n green,\n colorize,\n )},`,\n )\n .replace(/length of `(\\d+)`/u, `length of ${color('$1', red, colorize)}`)\n .replace(/a array/u, 'an array');\n\n return `${prefix}${message}.`;\n }\n\n // Refinements we built ourselves have nice error messages\n if (failure.refinement !== undefined) {\n return `${prefix}${failure.message}.`;\n }\n\n return `${prefix}Expected a value of type ${color(\n failure.type,\n green,\n colorize,\n )}, but received: ${received}.`;\n}\n\n/**\n * Get a string describing the errors. This formats all the errors in a\n * human-readable way.\n *\n * @param struct - The struct that caused the failures.\n * @param failures - The `superstruct` failures.\n * @param colorize - Whether to colorize the value. Defaults to `true`.\n * @returns A string describing the errors.\n */\nexport function getStructErrorMessage<Type, Schema>(\n struct: Struct<Type, Schema>,\n failures: Failure[],\n colorize = true,\n) {\n const formattedFailures = failures.map((failure) =>\n indent(`• ${getStructFailureMessage(struct, failure, colorize)}`),\n );\n\n return formattedFailures.join('\\n');\n}\n\n/**\n * Validate a union struct, and throw readable errors if the value does not\n * satisfy the struct. This is useful for improving the error messages returned\n * by `superstruct`.\n *\n * @param value - The value to validate.\n * @param struct - The `superstruct` union struct to validate the value against.\n * This struct must be a union of object structs, and must have at least one\n * shared key to validate against.\n * @param structKey - The key to validate against. This key must be present in\n * all the object structs in the union struct, and is expected to be a literal\n * value.\n * @param coerce - Whether to coerce the value to satisfy the struct. Defaults\n * to `false`.\n * @returns The validated value.\n * @throws If the value does not satisfy the struct.\n * @example\n * const struct = union([\n * object({ type: literal('a'), value: string() }),\n * object({ type: literal('b'), value: number() }),\n * object({ type: literal('c'), value: boolean() }),\n * // ...\n * ]);\n *\n * // At path: type — Expected the value to be one of: \"a\", \"b\", \"c\", but received: \"d\".\n * validateUnion({ type: 'd', value: 'foo' }, struct, 'type');\n *\n * // At path: value — Expected a value of type string, but received: 42.\n * validateUnion({ type: 'a', value: 42 }, struct, 'value');\n */\nexport function validateUnion<Type, Schema extends readonly Struct<any, any>[]>(\n value: unknown,\n struct: Struct<Type, Schema>,\n structKey: keyof Type,\n coerce = false,\n) {\n assert(\n struct.schema,\n 'Expected a struct with a schema. Make sure to use `union` from `@metamask/snaps-sdk`.',\n );\n assert(struct.schema.length > 0, 'Expected a non-empty array of structs.');\n\n const keyUnion = struct.schema.map(\n (innerStruct) => innerStruct.schema[structKey],\n // This is guaranteed to be a non-empty array by the assertion above. We\n // need to cast it since `superstruct` requires a non-empty array of structs\n // for the `union` struct.\n ) as NonEmptyArray<Struct>;\n\n const key = superstructType({\n [structKey]: union(keyUnion),\n });\n\n const [keyError] = validate(value, key, { coerce });\n if (keyError) {\n throw new Error(\n getStructFailureMessage(key, keyError.failures()[0], false),\n );\n }\n\n // At this point it's guaranteed that the value is an object, so we can safely\n // cast it to a Record.\n const objectValue = value as Record<PropertyKey, unknown>;\n const objectStructs = struct.schema.filter((innerStruct) =>\n is(objectValue[structKey], innerStruct.schema[structKey]),\n );\n\n assert(objectStructs.length > 0, 'Expected a struct to match the value.');\n\n // We need to validate the value against all the object structs that match the\n // struct key, and return the first validated value.\n const validationResults = objectStructs.map((objectStruct) =>\n validate(objectValue, objectStruct, { coerce }),\n );\n\n const validatedValue = validationResults.find(([error]) => !error);\n if (validatedValue) {\n return validatedValue[1];\n }\n\n assert(validationResults[0][0], 'Expected at least one error.');\n\n // If there is no validated value, we need to find the error with the least\n // number of failures (with the assumption that it's the most specific error).\n const validationError = validationResults.reduce((error, [innerError]) => {\n assert(innerError, 'Expected an error.');\n if (innerError.failures().length < error.failures().length) {\n return innerError;\n }\n\n return error;\n }, validationResults[0][0]);\n\n throw new Error(\n getStructFailureMessage(struct, validationError.failures()[0], false),\n );\n}\n\n/**\n * Create a value with the coercion logic of a union struct, and throw readable\n * errors if the value does not satisfy the struct. This is useful for improving\n * the error messages returned by `superstruct`.\n *\n * @param value - The value to validate.\n * @param struct - The `superstruct` union struct to validate the value against.\n * This struct must be a union of object structs, and must have at least one\n * shared key to validate against.\n * @param structKey - The key to validate against. This key must be present in\n * all the object structs in the union struct, and is expected to be a literal\n * value.\n * @returns The validated value.\n * @throws If the value does not satisfy the struct.\n * @see validateUnion\n */\nexport function createUnion<Type, Schema extends readonly Struct<any, any>[]>(\n value: unknown,\n struct: Struct<Type, Schema>,\n structKey: keyof Type,\n) {\n return validateUnion(value, struct, structKey, true);\n}\n\n// These types are copied from Superstruct, to mirror `assign`.\nexport function mergeStructs<\n ObjectA extends ObjectSchema,\n ObjectB extends ObjectSchema,\n>(\n A: Struct<ObjectType<ObjectA>, ObjectA>,\n B: Struct<ObjectType<ObjectB>, ObjectB>,\n): Struct<ObjectType<Assign<ObjectA, ObjectB>>, Assign<ObjectA, ObjectB>>;\nexport function mergeStructs<\n ObjectA extends ObjectSchema,\n ObjectB extends ObjectSchema,\n ObjectC extends ObjectSchema,\n>(\n A: Struct<ObjectType<ObjectA>, ObjectA>,\n B: Struct<ObjectType<ObjectB>, ObjectB>,\n C: Struct<ObjectType<ObjectC>, ObjectC>,\n): Struct<\n ObjectType<Assign<Assign<ObjectA, ObjectB>, ObjectC>>,\n Assign<Assign<ObjectA, ObjectB>, ObjectC>\n>;\nexport function mergeStructs<\n ObjectA extends ObjectSchema,\n ObjectB extends ObjectSchema,\n ObjectC extends ObjectSchema,\n ObjectD extends ObjectSchema,\n>(\n A: Struct<ObjectType<ObjectA>, ObjectA>,\n B: Struct<ObjectType<ObjectB>, ObjectB>,\n C: Struct<ObjectType<ObjectC>, ObjectC>,\n D: Struct<ObjectType<ObjectD>, ObjectD>,\n): Struct<\n ObjectType<Assign<Assign<Assign<ObjectA, ObjectB>, ObjectC>, ObjectD>>,\n Assign<Assign<Assign<ObjectA, ObjectB>, ObjectC>, ObjectD>\n>;\nexport function mergeStructs<\n ObjectA extends ObjectSchema,\n ObjectB extends ObjectSchema,\n ObjectC extends ObjectSchema,\n ObjectD extends ObjectSchema,\n ObjectE extends ObjectSchema,\n>(\n A: Struct<ObjectType<ObjectA>, ObjectA>,\n B: Struct<ObjectType<ObjectB>, ObjectB>,\n C: Struct<ObjectType<ObjectC>, ObjectC>,\n D: Struct<ObjectType<ObjectD>, ObjectD>,\n E: Struct<ObjectType<ObjectE>, ObjectE>,\n): Struct<\n ObjectType<\n Assign<Assign<Assign<Assign<ObjectA, ObjectB>, ObjectC>, ObjectD>, ObjectE>\n >,\n Assign<Assign<Assign<Assign<ObjectA, ObjectB>, ObjectC>, ObjectD>, ObjectE>\n>;\n\n/**\n * Merge multiple structs into one, using superstruct `assign`.\n *\n * Differently from plain `assign`, this function also copies over refinements from each struct.\n *\n * @param structs - The `superstruct` structs to merge.\n * @returns The merged struct.\n */\nexport function mergeStructs(...structs: Struct<any>[]): Struct<any> {\n const mergedStruct = (assign as (...structs: Struct<any>[]) => Struct)(\n ...structs,\n );\n return new Struct({\n ...mergedStruct,\n *refiner(value, ctx) {\n for (const struct of structs) {\n yield* struct.refiner(value, ctx);\n }\n },\n });\n}\n"]}