{"version":3,"file":"zoderrors.js","sources":["../../../src/integrations/zoderrors.ts"],"sourcesContent":["import { defineIntegration } from '../integration';\nimport type { Event, EventHint } from '../types-hoist/event';\nimport type { IntegrationFn } from '../types-hoist/integration';\nimport { isError } from '../utils/is';\nimport { truncate } from '../utils/string';\n\ninterface ZodErrorsOptions {\n  key?: string;\n  /**\n   * Limits the number of Zod errors inlined in each Sentry event.\n   *\n   * @default 10\n   */\n  limit?: number;\n  /**\n   * Save full list of Zod issues as an attachment in Sentry\n   *\n   * @default false\n   */\n  saveZodIssuesAsAttachment?: boolean;\n}\n\nconst DEFAULT_LIMIT = 10;\nconst INTEGRATION_NAME = 'ZodErrors';\n\n/**\n * Simplified ZodIssue type definition\n */\ninterface ZodIssue {\n  path: (string | number)[];\n  message?: string;\n  expected?: unknown;\n  received?: unknown;\n  unionErrors?: unknown[];\n  keys?: unknown[];\n  invalid_literal?: unknown;\n}\n\ninterface ZodError extends Error {\n  issues: ZodIssue[];\n}\n\nfunction originalExceptionIsZodError(originalException: unknown): originalException is ZodError {\n  return (\n    isError(originalException) &&\n    originalException.name === 'ZodError' &&\n    Array.isArray((originalException as ZodError).issues)\n  );\n}\n\ntype SingleLevelZodIssue<T extends ZodIssue> = {\n  [P in keyof T]: T[P] extends string | number | undefined\n    ? T[P]\n    : T[P] extends unknown[]\n      ? string | undefined\n      : unknown;\n};\n\n/**\n * Formats child objects or arrays to a string\n * that is preserved when sent to Sentry.\n *\n * Without this, we end up with something like this in Sentry:\n *\n * [\n *  [Object],\n *  [Object],\n *  [Object],\n *  [Object]\n * ]\n */\nexport function flattenIssue(issue: ZodIssue): SingleLevelZodIssue<ZodIssue> {\n  return {\n    ...issue,\n    path: 'path' in issue && Array.isArray(issue.path) ? issue.path.join('.') : undefined,\n    keys: 'keys' in issue ? JSON.stringify(issue.keys) : undefined,\n    unionErrors: 'unionErrors' in issue ? JSON.stringify(issue.unionErrors) : undefined,\n  };\n}\n\n/**\n * Takes ZodError issue path array and returns a flattened version as a string.\n * This makes it easier to display paths within a Sentry error message.\n *\n * Array indexes are normalized to reduce duplicate entries\n *\n * @param path ZodError issue path\n * @returns flattened path\n *\n * @example\n * flattenIssuePath([0, 'foo', 1, 'bar']) // -> '<array>.foo.<array>.bar'\n */\nexport function flattenIssuePath(path: Array<string | number>): string {\n  return path\n    .map(p => {\n      if (typeof p === 'number') {\n        return '<array>';\n      } else {\n        return p;\n      }\n    })\n    .join('.');\n}\n\n/**\n * Zod error message is a stringified version of ZodError.issues\n * This doesn't display well in the Sentry UI. Replace it with something shorter.\n */\nexport function formatIssueMessage(zodError: ZodError): string {\n  const errorKeyMap = new Set<string | number | symbol>();\n  for (const iss of zodError.issues) {\n    const issuePath = flattenIssuePath(iss.path);\n    if (issuePath.length > 0) {\n      errorKeyMap.add(issuePath);\n    }\n  }\n\n  const errorKeys = Array.from(errorKeyMap);\n  if (errorKeys.length === 0) {\n    // If there are no keys, then we're likely validating the root\n    // variable rather than a key within an object. This attempts\n    // to extract what type it was that failed to validate.\n    // For example, z.string().parse(123) would return \"string\" here.\n    let rootExpectedType = 'variable';\n    if (zodError.issues.length > 0) {\n      const iss = zodError.issues[0];\n      if (iss !== undefined && 'expected' in iss && typeof iss.expected === 'string') {\n        rootExpectedType = iss.expected;\n      }\n    }\n    return `Failed to validate ${rootExpectedType}`;\n  }\n  return `Failed to validate keys: ${truncate(errorKeys.join(', '), 100)}`;\n}\n\n/**\n * Applies ZodError issues to an event extra and replaces the error message\n */\nexport function applyZodErrorsToEvent(\n  limit: number,\n  saveZodIssuesAsAttachment: boolean = false,\n  event: Event,\n  hint: EventHint,\n): Event {\n  if (\n    !event.exception?.values ||\n    !hint.originalException ||\n    !originalExceptionIsZodError(hint.originalException) ||\n    hint.originalException.issues.length === 0\n  ) {\n    return event;\n  }\n\n  try {\n    const issuesToFlatten = saveZodIssuesAsAttachment\n      ? hint.originalException.issues\n      : hint.originalException.issues.slice(0, limit);\n    const flattenedIssues = issuesToFlatten.map(flattenIssue);\n\n    if (saveZodIssuesAsAttachment) {\n      // Sometimes having the full error details can be helpful.\n      // Attachments have much higher limits, so we can include the full list of issues.\n      if (!Array.isArray(hint.attachments)) {\n        hint.attachments = [];\n      }\n      hint.attachments.push({\n        filename: 'zod_issues.json',\n        data: JSON.stringify({\n          issues: flattenedIssues,\n        }),\n      });\n    }\n\n    return {\n      ...event,\n      exception: {\n        ...event.exception,\n        values: [\n          {\n            ...event.exception.values[0],\n            value: formatIssueMessage(hint.originalException),\n          },\n          ...event.exception.values.slice(1),\n        ],\n      },\n      extra: {\n        ...event.extra,\n        'zoderror.issues': flattenedIssues.slice(0, limit),\n      },\n    };\n  } catch (e) {\n    // Hopefully we never throw errors here, but record it\n    // with the event just in case.\n    return {\n      ...event,\n      extra: {\n        ...event.extra,\n        'zoderrors sentry integration parse error': {\n          message: 'an exception was thrown while processing ZodError within applyZodErrorsToEvent()',\n          error: e instanceof Error ? `${e.name}: ${e.message}\\n${e.stack}` : 'unknown',\n        },\n      },\n    };\n  }\n}\n\nconst _zodErrorsIntegration = ((options: ZodErrorsOptions = {}) => {\n  const limit = options.limit ?? DEFAULT_LIMIT;\n\n  return {\n    name: INTEGRATION_NAME,\n    processEvent(originalEvent, hint): Event {\n      const processedEvent = applyZodErrorsToEvent(limit, options.saveZodIssuesAsAttachment, originalEvent, hint);\n      return processedEvent;\n    },\n  };\n}) satisfies IntegrationFn;\n\n/**\n * Sentry integration to process Zod errors, making them easier to work with in Sentry.\n */\nexport const zodErrorsIntegration = defineIntegration(_zodErrorsIntegration);\n"],"names":["isError","truncate","defineIntegration"],"mappings":"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"}