{"version":3,"file":"schemaValidator.js","sourceRoot":"","sources":["../../../src/util/config/schemaValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import Ajv, { ErrorObject, ValidateFunction } from \"ajv\"\n\nexport type PostFormatter = (formattedError: string, error: ErrorObject) => string\n\nexport interface ValidationConfig {\n  name?: string\n  postFormatter?: PostFormatter\n}\n\nconst ajv = new Ajv({\n  allErrors: true,\n  verbose: true,\n  coerceTypes: true,\n  strict: false,\n})\n\n// Cache the compiled validator for the canonical scheme.json so it is only\n// compiled once per process lifetime.\nlet _cachedValidate: ValidateFunction | undefined\n\nexport function validateSchema(schema: unknown, data: unknown, config: ValidationConfig = {}): void {\n  if (_cachedValidate == null || _cachedValidate.schema !== schema) {\n    _cachedValidate = ajv.compile(schema as object)\n  }\n  const validate = _cachedValidate\n\n  if (validate(data)) {\n    return\n  }\n\n  const errors = validate.errors ?? []\n  const baseDataPath = \"configuration\"\n  const name = config.name ?? \"Object\"\n\n  const relevant = filterRelevantErrors(errors)\n  const formatted = relevant.map(error => {\n    let msg = formatError(error, baseDataPath)\n    if (config.postFormatter != null) {\n      msg = config.postFormatter(msg, error)\n    }\n    return ` - ${indentNewlines(msg, \"   \")}`\n  })\n\n  const header = `Invalid configuration object. ${name} has been initialized using a configuration object that does not match the API schema.`\n  throw new Error(`${header}\\n${formatted.join(\"\\n\")}`)\n}\n\n/**\n * Filters the flat list of ajv errors to the most actionable subset.\n * Composite keywords (anyOf/oneOf/if) are suppressed when more specific\n * errors exist at the same or deeper paths.  When all errors share the\n * same instancePath and a composite parent for that path exists, the\n * parent is kept instead so the user sees a single \"should be one of\"\n * message rather than every failed branch listed separately.\n */\nfunction filterRelevantErrors(errors: ErrorObject[]): ErrorObject[] {\n  const compositeKeywords = new Set([\"anyOf\", \"oneOf\", \"if\"])\n  const specific = errors.filter(e => !compositeKeywords.has(e.keyword))\n\n  if (specific.length === 0) {\n    return errors.filter(e => compositeKeywords.has(e.keyword))\n  }\n\n  // Group specific errors by instancePath\n  const byPath = new Map<string, ErrorObject[]>()\n  for (const e of specific) {\n    const list = byPath.get(e.instancePath)\n    if (list != null) {\n      list.push(e)\n    } else {\n      byPath.set(e.instancePath, [e])\n    }\n  }\n\n  const result: ErrorObject[] = []\n  for (const [path, group] of byPath) {\n    // When multiple branch-failures share the same path, prefer the anyOf\n    // parent error (one concise \"should be one of these\" message).\n    if (group.length > 1) {\n      const parent = errors.find(e => e.instancePath === path && compositeKeywords.has(e.keyword))\n      if (parent != null) {\n        result.push(parent)\n        continue\n      }\n    }\n    result.push(...group)\n  }\n  return result\n}\n\n/** Decodes a single RFC 6901 JSON Pointer segment (`~1` → `/`, `~0` → `~`). */\nfunction decodePointerSegment(segment: string): string {\n  return segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\")\n}\n\nfunction instancePathToLabel(instancePath: string, baseDataPath: string): string {\n  if (!instancePath) {\n    return baseDataPath\n  }\n  const segments = instancePath.split(\"/\").filter(Boolean).map(decodePointerSegment)\n  return [baseDataPath, ...segments].join(\".\")\n}\n\nfunction formatError(error: ErrorObject, baseDataPath: string): string {\n  const label = instancePathToLabel(error.instancePath, baseDataPath)\n  const params = error.params as Record<string, unknown>\n  const parentSchema = (error as ErrorObject & { parentSchema?: Record<string, unknown> }).parentSchema\n\n  switch (error.keyword) {\n    case \"additionalProperties\":\n      return `${label} has an unknown property '${params.additionalProperty}'`\n\n    case \"required\": {\n      const missingProp = String(params.missingProperty).replace(/^\\./, \"\")\n      return `${label} misses the property '${missingProp}'`\n    }\n\n    case \"type\": {\n      const type = params.type\n      if (Array.isArray(type)) {\n        const typeList = (type as string[]).join(\" | \")\n        const desc = descriptionSuffix(parentSchema)\n        return `${label} should be:\\n${typeList}${desc}`\n      }\n      return `${label} should be a ${type}`\n    }\n\n    case \"enum\": {\n      const enumVals = (parentSchema?.enum as unknown[] | undefined) ?? []\n      if (enumVals.length === 1) {\n        return `${label} should be ${JSON.stringify(enumVals[0])}`\n      }\n      return `${label} should be one of these:\\n${enumVals.map(v => JSON.stringify(v)).join(\" | \")}`\n    }\n\n    case \"anyOf\":\n    case \"oneOf\": {\n      const schemaText = formatSchemaType(parentSchema)\n      return `${label} should be one of these:\\n${schemaText}`\n    }\n\n    default:\n      return `${label} ${error.message}`\n  }\n}\n\nfunction descriptionSuffix(schema: Record<string, unknown> | undefined): string {\n  return typeof schema?.description === \"string\" ? `\\n-> ${schema.description}` : \"\"\n}\n\nfunction formatSchemaType(schema: Record<string, unknown> | undefined): string {\n  if (schema == null) {\n    return \"\"\n  }\n  if (Array.isArray(schema.type)) {\n    return (schema.type as string[]).join(\" | \")\n  }\n  if (typeof schema.type === \"string\") {\n    return schema.type\n  }\n  if (Array.isArray(schema.anyOf)) {\n    return (schema.anyOf as Array<Record<string, unknown>>)\n      .map(s => {\n        if (Array.isArray(s.type)) {\n          return (s.type as string[]).join(\" | \")\n        }\n        return typeof s.type === \"string\" ? s.type : \"\"\n      })\n      .filter(Boolean)\n      .join(\" | \")\n  }\n  return JSON.stringify(schema)\n}\n\nfunction indentNewlines(str: string, prefix: string): string {\n  return str.replace(/\\n(?!$)/g, `\\n${prefix}`)\n}\n"]}