« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things/validators.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things/validators.js')
-rw-r--r--src/data/things/validators.js984
1 files changed, 0 insertions, 984 deletions
diff --git a/src/data/things/validators.js b/src/data/things/validators.js
deleted file mode 100644
index efe76fe0..00000000
--- a/src/data/things/validators.js
+++ /dev/null
@@ -1,984 +0,0 @@
-import {inspect as nodeInspect} from 'node:util';
-
-// Heresy.
-import printable_characters from 'printable-characters';
-const {strlen} = printable_characters;
-
-import {colors, ENABLE_COLOR} from '#cli';
-import {commentaryRegex} from '#wiki-data';
-
-import {
-  cut,
-  empty,
-  matchMultiline,
-  openAggregate,
-  typeAppearance,
-  withAggregate,
-} from '#sugar';
-
-function inspect(value) {
-  return nodeInspect(value, {colors: ENABLE_COLOR});
-}
-
-export function getValidatorCreator(validator) {
-  return validator[Symbol.for(`hsmusic.validator.creator`)] ?? null;
-}
-
-export function getValidatorCreatorMeta(validator) {
-  return validator[Symbol.for(`hsmusic.validator.creatorMeta`)] ?? null;
-}
-
-export function setValidatorCreatorMeta(validator, creator, meta) {
-  validator[Symbol.for(`hsmusic.validator.creator`)] = creator;
-  validator[Symbol.for(`hsmusic.validator.creatorMeta`)] = meta;
-  return validator;
-}
-
-// Basic types (primitives)
-
-export function a(noun) {
-  return /[aeiou]/.test(noun[0]) ? `an ${noun}` : `a ${noun}`;
-}
-
-export function validateType(type) {
-  const fn = value => {
-    if (typeof value !== type)
-      throw new TypeError(`Expected ${a(type)}, got ${typeAppearance(value)}`);
-
-    return true;
-  };
-
-  setValidatorCreatorMeta(fn, validateType, {type});
-
-  return fn;
-}
-
-export const isBoolean =
-  validateType('boolean');
-
-export const isFunction =
-  validateType('function');
-
-export const isNumber =
-  validateType('number');
-
-export const isString =
-  validateType('string');
-
-export const isSymbol =
-  validateType('symbol');
-
-// Use isObject instead, which disallows null.
-export const isTypeofObject =
-  validateType('object');
-
-export function isPositive(number) {
-  isNumber(number);
-
-  if (number <= 0) throw new TypeError(`Expected positive number`);
-
-  return true;
-}
-
-export function isNegative(number) {
-  isNumber(number);
-
-  if (number >= 0) throw new TypeError(`Expected negative number`);
-
-  return true;
-}
-
-export function isPositiveOrZero(number) {
-  isNumber(number);
-
-  if (number < 0) throw new TypeError(`Expected positive number or zero`);
-
-  return true;
-}
-
-export function isNegativeOrZero(number) {
-  isNumber(number);
-
-  if (number > 0) throw new TypeError(`Expected negative number or zero`);
-
-  return true;
-}
-
-export function isInteger(number) {
-  isNumber(number);
-
-  if (number % 1 !== 0) throw new TypeError(`Expected integer`);
-
-  return true;
-}
-
-export function isCountingNumber(number) {
-  isInteger(number);
-  isPositive(number);
-
-  return true;
-}
-
-export function isWholeNumber(number) {
-  isInteger(number);
-  isPositiveOrZero(number);
-
-  return true;
-}
-
-export function isStringNonEmpty(value) {
-  isString(value);
-
-  if (value.trim().length === 0)
-    throw new TypeError(`Expected non-empty string`);
-
-  return true;
-}
-
-export function optional(validator) {
-  return value =>
-    value === null ||
-    value === undefined ||
-    validator(value);
-}
-
-// Complex types (non-primitives)
-
-export function isInstance(value, constructor) {
-  isObject(value);
-
-  if (!(value instanceof constructor))
-    throw new TypeError(`Expected ${constructor.name}, got ${value.constructor.name}`);
-
-  return true;
-}
-
-export function isDate(value) {
-  isInstance(value, Date);
-
-  if (isNaN(value))
-    throw new TypeError(`Expected valid date`);
-
-  return true;
-}
-
-export function isObject(value) {
-  isTypeofObject(value);
-
-  // Note: Please remember that null is always a valid value for properties
-  // held by a CacheableObject. This assertion is exclusively for use in other
-  // contexts.
-  if (value === null)
-    throw new TypeError(`Expected an object, got null`);
-
-  return true;
-}
-
-export function isArray(value) {
-  if (typeof value !== 'object' || value === null || !Array.isArray(value))
-    throw new TypeError(`Expected an array, got ${typeAppearance(value)}`);
-
-  return true;
-}
-
-// This one's shaped a bit different from other "is" functions.
-// More like validate functions, it returns a function.
-export function is(...values) {
-  if (Array.isArray(values)) {
-    values = new Set(values);
-  }
-
-  if (values.size === 1) {
-    const expected = Array.from(values)[0];
-
-    return (value) => {
-      if (value !== expected) {
-        throw new TypeError(`Expected ${expected}, got ${value}`);
-      }
-
-      return true;
-    };
-  }
-
-  const fn = (value) => {
-    if (!values.has(value)) {
-      throw new TypeError(`Expected one of ${Array.from(values).join(' ')}, got ${value}`);
-    }
-
-    return true;
-  };
-
-  setValidatorCreatorMeta(fn, is, {values});
-
-  return fn;
-}
-
-function validateArrayItemsHelper(itemValidator) {
-  return (item, index, array) => {
-    try {
-      const value = itemValidator(item, index, array);
-
-      if (value !== true) {
-        throw new Error(`Expected validator to return true`);
-      }
-    } catch (caughtError) {
-      const indexPart = colors.yellow(`zero-index ${index}`)
-      const itemPart = inspect(item);
-      const message = `Error at ${indexPart}: ${itemPart}`;
-      const error = new Error(message, {cause: caughtError});
-      error[Symbol.for('hsmusic.annotateError.indexInSourceArray')] = index;
-      throw error;
-    }
-  };
-}
-
-export function validateArrayItems(itemValidator) {
-  const helper = validateArrayItemsHelper(itemValidator);
-
-  return (array) => {
-    isArray(array);
-
-    withAggregate({message: 'Errors validating array items'}, ({call}) => {
-      for (let index = 0; index < array.length; index++) {
-        call(helper, array[index], index, array);
-      }
-    });
-
-    return true;
-  };
-}
-
-export function strictArrayOf(itemValidator) {
-  return validateArrayItems(itemValidator);
-}
-
-export function sparseArrayOf(itemValidator) {
-  return validateArrayItems((item, index, array) => {
-    if (item === false || item === null) {
-      return true;
-    }
-
-    return itemValidator(item, index, array);
-  });
-}
-
-export function looseArrayOf(itemValidator) {
-  return validateArrayItems((item, index, array) => {
-    if (item === false || item === null || item === undefined) {
-      return true;
-    }
-
-    return itemValidator(item, index, array);
-  });
-}
-
-export function validateInstanceOf(constructor) {
-  const fn = (object) => isInstance(object, constructor);
-
-  setValidatorCreatorMeta(fn, validateInstanceOf, {constructor});
-
-  return fn;
-}
-
-// Wiki data (primitives & non-primitives)
-
-export function isColor(color) {
-  isStringNonEmpty(color);
-
-  if (color.startsWith('#')) {
-    if (![4, 5, 7, 9].includes(color.length))
-      throw new TypeError(`Expected #rgb, #rgba, #rrggbb, or #rrggbbaa, got length ${color.length}`);
-
-    if (/[^0-9a-fA-F]/.test(color.slice(1)))
-      throw new TypeError(`Expected hexadecimal digits`);
-
-    return true;
-  }
-
-  throw new TypeError(`Unknown color format`);
-}
-
-export function isCommentary(commentaryText) {
-  isContentString(commentaryText);
-
-  const rawMatches =
-    Array.from(commentaryText.matchAll(commentaryRegex));
-
-  if (empty(rawMatches)) {
-    throw new TypeError(`Expected at least one commentary heading`);
-  }
-
-  const niceMatches =
-    rawMatches.map(match => ({
-      position: match.index,
-      length: match[0].length,
-    }));
-
-  validateArrayItems(({position, length}, index) => {
-    if (index === 0 && position > 0) {
-      throw new TypeError(`Expected first commentary heading to be at top`);
-    }
-
-    const ownInput = commentaryText.slice(position, position + length);
-    const restOfInput = commentaryText.slice(position + length);
-    const nextLineBreak = restOfInput.indexOf('\n');
-    const upToNextLineBreak = restOfInput.slice(0, nextLineBreak);
-
-    if (/\S/.test(upToNextLineBreak)) {
-      throw new TypeError(
-        `Expected commentary heading to occupy entire line, got extra text:\n` +
-        `${colors.green(`"${cut(ownInput, 40)}"`)} (<- heading)\n` +
-        `(extra on same line ->) ${colors.red(`"${cut(upToNextLineBreak, 30)}"`)}\n` +
-        `(Check for missing "|-" in YAML, or a misshapen annotation)`);
-    }
-
-    const nextHeading =
-      (index === niceMatches.length - 1
-        ? commentaryText.length
-        : niceMatches[index + 1].position);
-
-    const upToNextHeading =
-      commentaryText.slice(position + length, nextHeading);
-
-    if (!/\S/.test(upToNextHeading)) {
-      throw new TypeError(
-        `Expected commentary entry to have body text, only got a heading`);
-    }
-
-    return true;
-  })(niceMatches);
-
-  return true;
-}
-
-const isArtistRef = validateReference('artist');
-
-export function validateProperties(spec) {
-  const {
-    [validateProperties.validateOtherKeys]: validateOtherKeys = null,
-    [validateProperties.allowOtherKeys]: allowOtherKeys = false,
-  } = spec;
-
-  const specEntries = Object.entries(spec);
-  const specKeys = Object.keys(spec);
-
-  return (object) => {
-    isObject(object);
-
-    if (Array.isArray(object))
-      throw new TypeError(`Expected an object, got array`);
-
-    withAggregate({message: `Errors validating object properties`}, ({push}) => {
-      const testEntries = specEntries.slice();
-
-      const unknownKeys = Object.keys(object).filter((key) => !specKeys.includes(key));
-      if (validateOtherKeys) {
-        for (const key of unknownKeys) {
-          testEntries.push([key, validateOtherKeys]);
-        }
-      }
-
-      for (const [specKey, specValidator] of testEntries) {
-        const value = object[specKey];
-        try {
-          specValidator(value);
-        } catch (caughtError) {
-          const keyPart = colors.green(specKey);
-          const valuePart = inspect(value);
-          const message = `Error for key ${keyPart}: ${valuePart}`;
-          push(new Error(message, {cause: caughtError}));
-        }
-      }
-
-      if (!validateOtherKeys && !allowOtherKeys && !empty(unknownKeys)) {
-        push(new Error(
-          `Unknown keys present (${unknownKeys.length}): [${unknownKeys.join(', ')}]`));
-      }
-    });
-
-    return true;
-  };
-}
-
-validateProperties.validateOtherKeys = Symbol();
-validateProperties.allowOtherKeys = Symbol();
-
-export const validateAllPropertyValues = (validator) =>
-  validateProperties({
-    [validateProperties.validateOtherKeys]: validator,
-  });
-
-const illeaglInvisibleSpace = {
-  action: 'delete',
-};
-
-const illegalVisibleSpace = {
-  action: 'replace',
-  with: ' ',
-  withAnnotation: `normal space`,
-};
-
-const illegalContentSpec = [
-  {illegal: '\u200b', annotation: `zero-width space`, ...illeaglInvisibleSpace},
-  {illegal: '\u2005', annotation: `four-per-em space`, ...illegalVisibleSpace},
-  {illegal: '\u205f', annotation: `medium mathematical space`, ...illegalVisibleSpace},
-  {illegal: '\xa0', annotation: `non-breaking space`, ...illegalVisibleSpace},
-];
-
-for (const entry of illegalContentSpec) {
-  entry.test = string =>
-    string.startsWith(entry.illegal);
-
-  if (entry.action === 'replace') {
-    entry.enact = string =>
-      string.replaceAll(entry.illegal, entry.with);
-  }
-}
-
-const illegalContentRegexp =
-  new RegExp(
-    illegalContentSpec
-      .map(entry => entry.illegal)
-      .map(illegal => `${illegal}+`)
-      .join('|'),
-    'g');
-
-const illegalCharactersInContent =
-  illegalContentSpec
-    .map(entry => entry.illegal)
-    .join('');
-
-const legalContentNearEndRegexp =
-  new RegExp(`[^\n${illegalCharactersInContent}]+$`);
-
-const legalContentNearStartRegexp =
-  new RegExp(`^[^\n${illegalCharactersInContent}]+`);
-
-const trimWhitespaceNearBothSidesRegexp =
-  /^ +| +$/gm;
-
-const trimWhitespaceNearEndRegexp =
-  / +$/gm;
-
-export function isContentString(content) {
-  isStringNonEmpty(content);
-
-  const mainAggregate = openAggregate({
-    message: `Errors validating content string`,
-    translucent: 'single',
-  });
-
-  const illegalAggregate = openAggregate({
-    message: `Illegal characters found in content string`,
-  });
-
-  for (const {match, where} of matchMultiline(content, illegalContentRegexp)) {
-    const {annotation, action, ...options} =
-      illegalContentSpec
-        .find(entry => entry.test(match[0]));
-
-    const matchStart = match.index;
-    const matchEnd = match.index + match[0].length;
-
-    const before =
-      content
-        .slice(Math.max(0, matchStart - 3), matchStart)
-        .match(legalContentNearEndRegexp)
-        ?.[0];
-
-    const after =
-      content
-        .slice(matchEnd, Math.min(content.length, matchEnd + 3))
-        .match(legalContentNearStartRegexp)
-        ?.[0];
-
-    const beforePart =
-      before && `"${before}"`;
-
-    const afterPart =
-      after && `"${after}"`;
-
-    const surroundings =
-      (before && after
-        ? `between ${beforePart} and ${afterPart}`
-     : before
-        ? `after ${beforePart}`
-     : after
-        ? `before ${afterPart}`
-        : ``);
-
-    const illegalPart =
-      colors.red(
-        (annotation
-          ? `"${match[0]}" (${annotation})`
-          : `"${match[0]}"`));
-
-    const replacement =
-      (action === 'replace'
-        ? options.enact(match[0])
-        : null);
-
-    const replaceWithPart =
-      (action === 'replace'
-        ? colors.green(
-            (options.withAnnotation
-              ? `"${replacement}" (${options.withAnnotation})`
-              : `"${replacement}"`))
-        : null);
-
-    const actionPart =
-      (action === `delete`
-        ? `Delete ${illegalPart}`
-     : action === 'replace'
-        ? `Replace ${illegalPart} with ${replaceWithPart}`
-        : `Matched ${illegalPart}`);
-
-    const parts = [
-      actionPart,
-      surroundings,
-      `(${where})`,
-    ].filter(Boolean);
-
-    illegalAggregate.push(new TypeError(parts.join(` `)));
-  }
-
-  const isMultiline = content.includes('\n');
-
-  const trimWhitespaceAggregate = openAggregate({
-    message:
-      (isMultiline
-        ? `Whitespace found at end of line`
-        : `Whitespace found at start or end`),
-  });
-
-  const trimWhitespaceRegexp =
-    (isMultiline
-      ? trimWhitespaceNearEndRegexp
-      : trimWhitespaceNearBothSidesRegexp);
-
-  for (
-    const {match, lineNumber, columnNumber, containingLine} of
-    matchMultiline(content, trimWhitespaceRegexp, {
-      formatWhere: false,
-      getContainingLine: true,
-    })
-  ) {
-    const linePart =
-      colors.yellow(`line ${lineNumber + 1}`);
-
-    const where =
-      (match[0].length === containingLine.length
-        ? `as all of ${linePart}`
-     : columnNumber === 0
-        ? (isMultiline
-            ? `at start of ${linePart}`
-            : `at start`)
-        : (isMultiline
-            ? `at end of ${linePart}`
-            : `at end`));
-
-    const whitespacePart =
-      colors.red(`"${match[0]}"`);
-
-    const parts = [
-      `Matched ${whitespacePart}`,
-      where,
-    ];
-
-    trimWhitespaceAggregate.push(new TypeError(parts.join(` `)));
-  }
-
-  mainAggregate.call(() => illegalAggregate.close());
-  mainAggregate.call(() => trimWhitespaceAggregate.close());
-  mainAggregate.close();
-
-  return true;
-}
-
-export function isThingClass(thingClass) {
-  isFunction(thingClass);
-
-  if (!Object.hasOwn(thingClass, Symbol.for('Thing.referenceType'))) {
-    throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`);
-  }
-
-  return true;
-}
-
-export const isContribution = validateProperties({
-  who: isArtistRef,
-  what: optional(isStringNonEmpty),
-});
-
-export const isContributionList = validateArrayItems(isContribution);
-
-export const isAdditionalFile = validateProperties({
-  title: isName,
-  description: optional(isContentString),
-  files: validateArrayItems(isString),
-});
-
-export const isAdditionalFileList = validateArrayItems(isAdditionalFile);
-
-export const isTrackSection = validateProperties({
-  name: optional(isName),
-  color: optional(isColor),
-  dateOriginallyReleased: optional(isDate),
-  isDefaultTrackSection: optional(isBoolean),
-  tracks: optional(validateReferenceList('track')),
-});
-
-export const isTrackSectionList = validateArrayItems(isTrackSection);
-
-export function isDimensions(dimensions) {
-  isArray(dimensions);
-
-  if (dimensions.length !== 2) throw new TypeError(`Expected 2 item array`);
-
-  isPositive(dimensions[0]);
-  isInteger(dimensions[0]);
-  isPositive(dimensions[1]);
-  isInteger(dimensions[1]);
-
-  return true;
-}
-
-export function isDirectory(directory) {
-  isStringNonEmpty(directory);
-
-  if (directory.match(/[^a-zA-Z0-9_-]/))
-    throw new TypeError(`Expected only letters, numbers, dash, and underscore, got "${directory}"`);
-
-  return true;
-}
-
-export function isDuration(duration) {
-  isNumber(duration);
-  isPositiveOrZero(duration);
-
-  return true;
-}
-
-export function isFileExtension(string) {
-  isStringNonEmpty(string);
-
-  if (string[0] === '.')
-    throw new TypeError(`Expected no dot (.) at the start of file extension`);
-
-  if (string.match(/[^a-zA-Z0-9_]/))
-    throw new TypeError(`Expected only alphanumeric and underscore`);
-
-  return true;
-}
-
-export function isLanguageCode(string) {
-  // TODO: This is a stub function because really we don't need a detailed
-  // is-language-code parser right now.
-
-  isString(string);
-
-  return true;
-}
-
-export function isName(name) {
-  return isContentString(name);
-}
-
-export function isURL(string) {
-  isStringNonEmpty(string);
-
-  new URL(string);
-
-  return true;
-}
-
-export function validateReference(type = 'track') {
-  return (ref) => {
-    isStringNonEmpty(ref);
-
-    const match = ref
-      .trim()
-      .match(/^(?:(?<typePart>\S+):(?=\S))?(?<directoryPart>.+)(?<!:)$/);
-
-    if (!match) throw new TypeError(`Malformed reference`);
-
-    const {groups: {typePart, directoryPart}} = match;
-
-    if (typePart) {
-      if (typePart !== type)
-        throw new TypeError(`Expected ref to begin with "${type}:", got "${typePart}:"`);
-
-      isDirectory(directoryPart);
-    }
-
-    isName(ref);
-
-    return true;
-  };
-}
-
-export function validateReferenceList(type = '') {
-  return validateArrayItems(validateReference(type));
-}
-
-const validateWikiData_cache = {};
-
-export function validateWikiData({
-  referenceType = '',
-  allowMixedTypes = false,
-}) {
-  if (referenceType && allowMixedTypes) {
-    throw new TypeError(`Don't specify both referenceType and allowMixedTypes`);
-  }
-
-  validateWikiData_cache[referenceType] ??= {};
-  validateWikiData_cache[referenceType][allowMixedTypes] ??= new WeakMap();
-
-  const isArrayOfObjects = validateArrayItems(isObject);
-
-  return (array) => {
-    const subcache = validateWikiData_cache[referenceType][allowMixedTypes];
-    if (subcache.has(array)) return subcache.get(array);
-
-    let OK = false;
-
-    try {
-      isArrayOfObjects(array);
-
-      if (empty(array)) {
-        OK = true; return true;
-      }
-
-      const allRefTypes = new Set();
-
-      let foundThing = false;
-      let foundOtherObject = false;
-
-      for (const object of array) {
-        const {[Symbol.for('Thing.referenceType')]: referenceType} = object.constructor;
-
-        if (referenceType === undefined) {
-          foundOtherObject = true;
-
-          // Early-exit if a Thing has been found - nothing more can be learned.
-          if (foundThing) {
-            throw new TypeError(`Expected array of wiki data objects, got mixed items`);
-          }
-        } else {
-          foundThing = true;
-
-          // Early-exit if a non-Thing object has been found - nothing more can
-          // be learned.
-          if (foundOtherObject) {
-            throw new TypeError(`Expected array of wiki data objects, got mixed items`);
-          }
-
-          allRefTypes.add(referenceType);
-        }
-      }
-
-      if (foundOtherObject && !foundThing) {
-        throw new TypeError(`Expected array of wiki data objects, got array of other objects`);
-      }
-
-      if (allRefTypes.size > 1) {
-        if (allowMixedTypes) {
-          OK = true; return true;
-        }
-
-        const types = () => Array.from(allRefTypes).join(', ');
-
-        if (referenceType) {
-          if (allRefTypes.has(referenceType)) {
-            allRefTypes.remove(referenceType);
-            throw new TypeError(`Expected array of only ${referenceType}, also got other types: ${types()}`)
-          } else {
-            throw new TypeError(`Expected array of only ${referenceType}, got other types: ${types()}`);
-          }
-        }
-
-        throw new TypeError(`Expected array of unmixed reference types, got multiple: ${types()}`);
-      }
-
-      const onlyRefType = Array.from(allRefTypes)[0];
-
-      if (referenceType && onlyRefType !== referenceType) {
-        throw new TypeError(`Expected array of ${referenceType}, got array of ${onlyRefType}`)
-      }
-
-      OK = true; return true;
-    } finally {
-      subcache.set(array, OK);
-    }
-  };
-}
-
-export const isAdditionalName = validateProperties({
-  name: isName,
-  annotation: optional(isContentString),
-
-  // TODO: This only allows indicating sourcing from a track.
-  // That's okay for the current limited use of "from", but
-  // could be expanded later.
-  from:
-    // Double TODO: Explicitly allowing both references and
-    // live objects to co-exist is definitely weird, and
-    // altogether questions the way we define validators...
-    optional(anyOf(
-      validateReferenceList('track'),
-      validateWikiData({referenceType: 'track'}))),
-});
-
-export const isAdditionalNameList = validateArrayItems(isAdditionalName);
-
-// Compositional utilities
-
-export function anyOf(...validators) {
-  const validConstants = new Set();
-  const validConstructors = new Set();
-  const validTypes = new Set();
-
-  const constantValidators = [];
-  const constructorValidators = [];
-  const typeValidators = [];
-
-  const leftoverValidators = [];
-
-  for (const validator of validators) {
-    const creator = getValidatorCreator(validator);
-    const creatorMeta = getValidatorCreatorMeta(validator);
-
-    switch (creator) {
-      case is:
-        for (const value of creatorMeta.values) {
-          validConstants.add(value);
-        }
-
-        constantValidators.push(validator);
-        break;
-
-      case validateInstanceOf:
-        validConstructors.add(creatorMeta.constructor);
-        constructorValidators.push(validator);
-        break;
-
-      case validateType:
-        validTypes.add(creatorMeta.type);
-        typeValidators.push(validator);
-        break;
-
-      default:
-        leftoverValidators.push(validator);
-        break;
-    }
-  }
-
-  return (value) => {
-    const errorInfo = [];
-
-    if (validConstants.has(value)) {
-      return true;
-    }
-
-    if (!empty(validTypes)) {
-      if (validTypes.has(typeof value)) {
-        return true;
-      }
-    }
-
-    for (const constructor of validConstructors) {
-      if (value instanceof constructor) {
-        return true;
-      }
-    }
-
-    for (const [i, validator] of leftoverValidators.entries()) {
-      try {
-        const result = validator(value);
-
-        if (result !== true) {
-          throw new Error(`Check returned false`);
-        }
-
-        return true;
-      } catch (error) {
-        errorInfo.push([validator, i, error]);
-      }
-    }
-
-    // Don't process error messages until every validator has failed.
-
-    const errors = [];
-    const prefaceErrorInfo = [];
-
-    let offset = 0;
-
-    if (!empty(validConstants)) {
-      const constants =
-        Array.from(validConstants);
-
-      const gotPart = `, got ${value}`;
-
-      prefaceErrorInfo.push([
-        constantValidators,
-        offset++,
-        new TypeError(
-          `Expected any of ${constants.join(' ')}` + gotPart),
-      ]);
-    }
-
-    if (!empty(validTypes)) {
-      const types =
-        Array.from(validTypes);
-
-      const gotType = typeAppearance(value);
-      const gotPart = `, got ${gotType}`;
-
-      prefaceErrorInfo.push([
-        typeValidators,
-        offset++,
-        new TypeError(
-          `Expected any of ${types.join(', ')}` + gotPart),
-      ]);
-    }
-
-    if (!empty(validConstructors)) {
-      const names =
-        Array.from(validConstructors)
-          .map(constructor => constructor.name);
-
-      const gotName = value?.constructor?.name;
-      const gotPart = (gotName ? `, got ${gotName}` : ``);
-
-      prefaceErrorInfo.push([
-        constructorValidators,
-        offset++,
-        new TypeError(
-          `Expected any of ${names.join(', ')}` + gotPart),
-      ]);
-    }
-
-    for (const info of errorInfo) {
-      info[1] += offset;
-    }
-
-    for (const [validator, i, error] of prefaceErrorInfo.concat(errorInfo)) {
-      error.message =
-        (validator?.name
-          ? `${i + 1}. "${validator.name}": ${error.message}`
-          : `${i + 1}. ${error.message}`);
-
-      error.check =
-        (Array.isArray(validator) && validator.length === 1
-          ? validator[0]
-          : validator);
-
-      errors.push(error);
-    }
-
-    const total = offset + leftoverValidators.length;
-    throw new AggregateError(errors,
-      `Expected any of ${total} possible checks, ` +
-      `but none were true`);
-  };
-}