{"version":3,"file":"error-to-human-readable.mjs","sources":["../src/error-to-human-readable.ts"],"sourcesContent":["import { DecodeJsonError, DecodeError } from './decode-json'\n\nconst wrapFieldName = (name: string): string => {\n  if (/^[a-z$_][0-9a-z$_]*$/i.test(name)) {\n    return `.${name}`\n  }\n\n  return `['${name}']`\n}\n\nconst spaces = (n: number): string => new Array(Math.max(0, n) + 1).join(' ')\n\nconst shiftRight = (n: number, text: string): string => {\n  const space = spaces(n)\n\n  return space + text.replace(/\\n/g, '\\n' + space).replace(/\\n\\s+\\n/g, '\\n\\n')\n}\n\nconst path = (context: Array<string>): string => `_${context.join('')}`\n\nconst stringifyJSON = (indent: number, source: unknown): string => {\n  return shiftRight(\n    indent,\n    typeof source === 'undefined'\n      ? 'undefined'\n      : JSON.stringify(source, null, indent)\n  )\n}\nconst problemWithValue = (context: Array<string>): string => {\n  return [\n    'Problem with ',\n    context.length === 0 ? 'the given value' : `a value at ${path(context)}`\n  ].join('')\n}\n\nconst expectingValue = (\n  prefix: string,\n  type: string,\n  optional: boolean\n): string => {\n  return [\n    'Expecting ',\n    optional ? `${prefix && 'an '}OPTIONAL ` : prefix,\n    type,\n    ' but actual value is'\n  ].join('')\n}\n\nconst oneOfToHumanReadable = (\n  errors: Array<DecodeError>,\n  optional: boolean,\n  indent: number,\n  context: Array<string>\n): string => {\n  if (errors.length === 0) {\n    return [\n      'Ran into oneOf with no possibilities',\n      context.length === 0 ? '' : ` at ${path(context)}`\n    ].join('')\n  }\n\n  if (errors.length === 1) {\n    return toHumanReadable(errors[0], optional, indent, context)\n  }\n\n  const lines: Array<string> = []\n\n  for (let index = 0; index < errors.length; index++) {\n    const line = toHumanReadable(errors[index], optional, indent, context)\n\n    lines.push(`(${index + 1}) ${line}`)\n  }\n\n  return [\n    'All possibilities of oneOf ',\n    context.length === 0 ? '' : `at ${path(context)} `,\n    `failed in the following ${errors.length} ways:\\n\\n`,\n    shiftRight(indent, lines.join('\\n\\n'))\n  ].join('')\n}\n\nconst runtimeExceptionToHumanReadable = (\n  runtimeError: Error,\n  indent: number,\n  context: Array<string>\n): string => {\n  return [\n    'Unexpected runtime error',\n    context.length === 0 ? '' : ` at ${path(context)}`,\n    ':\\n\\n',\n    spaces(indent),\n    runtimeError.message\n  ].join('')\n}\n\nconst requiredFieldToHumanReadable = (\n  fieldName: string,\n  source: Record<string, unknown>,\n  indent: number,\n  context: Array<string>\n): string => {\n  return [\n    problemWithValue(context),\n    '\\n',\n    `Expecting an OBJECT with a FIELD named '${fieldName}':`,\n    '\\n\\n',\n    stringifyJSON(indent, source)\n  ].join('')\n}\n\nconst requiredIndexToHumanReadable = (\n  position: number,\n  source: Array<unknown>,\n  indent: number,\n  context: Array<string>\n): string => {\n  return [\n    problemWithValue(context),\n    '\\n',\n    `Expecting an ARRAY with an ELEMENT at [${position}] but only see ${source.length} entries:`,\n    '\\n\\n',\n    stringifyJSON(indent, source)\n  ].join('')\n}\n\nconst endValueToHumanReadable = (\n  prefix: string,\n  type: string,\n  source: unknown,\n  optional: boolean,\n  indent: number,\n  context: Array<string>\n): string => {\n  return [\n    problemWithValue(context),\n    '\\n',\n    expectingValue(prefix, type, optional),\n    '\\n\\n',\n    stringifyJSON(indent, source)\n  ].join('')\n}\n\nconst exactToHumanReadable = (\n  value: string | number | boolean | null,\n  source: unknown,\n  optional: boolean,\n  indent: number,\n  context: Array<string>\n): string => {\n  return [\n    problemWithValue(context),\n    '\\n',\n    expectingValue('an ', `EXACT value ${JSON.stringify(value)}`, optional),\n    '\\n\\n',\n    stringifyJSON(indent, source)\n  ].join('')\n}\n\nconst failureToHumanReadable = (\n  template: string,\n  source: unknown,\n  indent: number,\n  context: Array<string>\n): string => {\n  return template\n    .replace(/{(path|context|location)}/g, path(context))\n    .replace(/{(source|json|value)}/g, stringifyJSON(indent, source))\n}\n\nconst toHumanReadable = (\n  error: DecodeError,\n  optional: boolean,\n  indent: number,\n  context: Array<string>\n): string => {\n  switch (error.type) {\n    case 'OPTIONAL': {\n      return toHumanReadable(error.error, true, indent, context)\n    }\n\n    case 'IN_FIELD': {\n      return toHumanReadable(error.error, false, indent, [\n        ...context,\n        wrapFieldName(error.name)\n      ])\n    }\n\n    case 'AT_INDEX': {\n      return toHumanReadable(error.error, false, indent, [\n        ...context,\n        `[${error.position}]`\n      ])\n    }\n\n    case 'ONE_OF': {\n      return oneOfToHumanReadable(error.errors, optional, indent, context)\n    }\n\n    case 'RUNTIME_EXCEPTION': {\n      return runtimeExceptionToHumanReadable(error.error, indent, context)\n    }\n\n    case 'REQUIRED_FIELD': {\n      return requiredFieldToHumanReadable(\n        error.name,\n        error.source,\n        indent,\n        context\n      )\n    }\n\n    case 'REQUIRED_INDEX': {\n      return requiredIndexToHumanReadable(\n        error.position,\n        error.source,\n        indent,\n        context\n      )\n    }\n\n    case 'FAILURE': {\n      return failureToHumanReadable(\n        error.message,\n        error.source,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_STRING': {\n      return endValueToHumanReadable(\n        'a ',\n        'STRING',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_BOOLEAN': {\n      return endValueToHumanReadable(\n        'a ',\n        'BOOLEAN',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_INT': {\n      return endValueToHumanReadable(\n        'an ',\n        'INTEGER',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_FLOAT': {\n      return endValueToHumanReadable(\n        'a ',\n        'FLOAT',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_OBJECT': {\n      return endValueToHumanReadable(\n        'an ',\n        'OBJECT',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_ARRAY': {\n      return endValueToHumanReadable(\n        'an ',\n        'ARRAY',\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n\n    case 'EXPECT_EXACT': {\n      return exactToHumanReadable(\n        error.value,\n        error.source,\n        optional,\n        indent,\n        context\n      )\n    }\n  }\n}\n\nconst errorToHumanReadable = (\n  error: DecodeJsonError,\n  { indent = 4 }: { indent?: number } = {}\n): string => {\n  if (error.type === 'INVALID_JSON') {\n    return [\n      'JSON parse error: ',\n      error.error.message,\n      '.\\n\\n',\n      spaces(indent),\n      error.source\n    ].join('')\n  }\n\n  return toHumanReadable(error, false, indent, [])\n}\n\nexport default errorToHumanReadable\n"],"names":["spaces","n","Array","Math","max","join","shiftRight","text","space","replace","path","context","stringifyJSON","indent","source","JSON","stringify","problemWithValue","length","expectingValue","prefix","type","optional","endValueToHumanReadable","toHumanReadable","error","name","test","position","errors","lines","index","line","push","oneOfToHumanReadable","runtimeError","message","runtimeExceptionToHumanReadable","fieldName","requiredFieldToHumanReadable","requiredIndexToHumanReadable","template","failureToHumanReadable","value","exactToHumanReadable"],"mappings":"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"}