validateSchema.js 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. // @ts-check
  2. const { isZodErrorLike } = require('zod-validation-error')
  3. /**
  4. * @typedef {import('zod').ZodType} ZodType
  5. */
  6. /**
  7. * @template T
  8. * @typedef {import('zod').output<T>} output<T>
  9. */
  10. /**
  11. * A helper function to safely get a nested value from an object
  12. * using a path array (e.g., ["query", "resource_type"])
  13. */
  14. function getPathValue(data, path) {
  15. let current = data
  16. for (const key of path) {
  17. if (current === null || typeof current !== 'object') {
  18. return undefined
  19. }
  20. current = current[key]
  21. }
  22. return current
  23. }
  24. const isRequiredError = (issue, value) =>
  25. value === undefined &&
  26. (issue.code === 'invalid_type' || issue.code === 'invalid_union')
  27. /**
  28. * Validates data against a Zod schema and throws a user-friendly error.
  29. *
  30. * @template {ZodType} T
  31. * @param {T} schema - The Zod schema
  32. * @param {unknown} data - The data to validate
  33. * @returns {output<T>} The validated (and transformed) data
  34. */
  35. function validateSchema(schema, data) {
  36. try {
  37. return schema.parse(data)
  38. } catch (err) {
  39. if (isZodErrorLike(err)) {
  40. const errorMessages = err.issues.map(issue => {
  41. const value = getPathValue(data, issue.path)
  42. const fieldName = String(issue.path[issue.path.length - 1])
  43. if (isRequiredError(issue, value)) {
  44. return `"${fieldName}" is required`
  45. }
  46. return `"${fieldName}" - ` + issue.message
  47. })
  48. throw new Error(errorMessages.join('; '))
  49. }
  50. throw err
  51. }
  52. }
  53. module.exports = { validateSchema }