validateSchema.js 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  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. * @param {any} data
  14. * @param {Array<PropertyKey>} path
  15. */
  16. function getPathValue(data, path) {
  17. let current = data
  18. for (const key of path) {
  19. if (current === null || typeof current !== 'object') {
  20. return undefined
  21. }
  22. current = current[key]
  23. }
  24. return current
  25. }
  26. /**
  27. * @param {any} issue
  28. * @param {any} value
  29. */
  30. const isRequiredError = (issue, value) =>
  31. value === undefined &&
  32. (issue.code === 'invalid_type' || issue.code === 'invalid_union')
  33. /**
  34. * Validates data against a Zod schema and throws a user-friendly error.
  35. *
  36. * @template {ZodType} T
  37. * @param {T} schema - The Zod schema
  38. * @param {unknown} data - The data to validate
  39. * @returns {output<T>} The validated (and transformed) data
  40. */
  41. function validateSchema(schema, data) {
  42. try {
  43. return schema.parse(data)
  44. } catch (err) {
  45. if (isZodErrorLike(err)) {
  46. const errorMessages = err.issues.map(issue => {
  47. const value = getPathValue(data, issue.path)
  48. const fieldName = String(issue.path[issue.path.length - 1])
  49. if (isRequiredError(issue, value)) {
  50. return `"${fieldName}" is required`
  51. }
  52. return `"${fieldName}" - ` + issue.message
  53. })
  54. throw new Error(errorMessages.join('; '))
  55. }
  56. throw err
  57. }
  58. }
  59. module.exports = { validateSchema }