{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Validator } from '@cfworker/json-schema';\nimport Ajv, { type ErrorObject, type JSONSchemaType } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport { z } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport { getGenkitRuntimeConfig } from './config.mjs';\nimport { GenkitError } from './error.mjs';\n\nimport type { Registry } from './registry.mjs';\nconst ajv = new Ajv();\naddFormats(ajv);\n\nexport { z }; // provide a consistent zod to use throughout genkit\n\n/**\n * JSON schema.\n */\nexport type JSONSchema = JSONSchemaType<any> | any;\n\nconst jsonSchemas = new WeakMap<z.ZodTypeAny, JSONSchema>();\nconst ajvValidators = new WeakMap<JSONSchema, ReturnType<typeof ajv.compile>>();\nconst cfWorkerValidators = new WeakMap<JSONSchema, Validator>();\nconst schemaAnnotations = new WeakMap<z.ZodTypeAny, Record<string, any>>();\n\n/**\n * Annotates a Zod schema with UI-specific metadata.\n *\n * NOTE: It's typically recommended to use x-genkit-* (or similar) as the prefix.\n */\nexport function annotateSchema<T extends z.ZodTypeAny>(\n  schema: T,\n  annotations: Record<string, any>\n): T {\n  const current = schemaAnnotations.get(schema) || {};\n  schemaAnnotations.set(schema, { ...current, ...annotations });\n  return schema;\n}\n\n/**\n * Wrapper object for various ways schema can be provided.\n */\nexport interface ProvidedSchema {\n  jsonSchema?: JSONSchema;\n  schema?: z.ZodTypeAny;\n}\n\n/**\n * Schema validation error.\n */\nexport class ValidationError extends GenkitError {\n  constructor({\n    data,\n    errors,\n    schema,\n  }: {\n    data: any;\n    errors: ValidationErrorDetail[];\n    schema: JSONSchema;\n  }) {\n    super({\n      status: 'INVALID_ARGUMENT',\n      message: `Schema validation failed. Parse Errors:\\n\\n${errors.map((e) => `- ${e.path}: ${e.message}`).join('\\n')}\\n\\nProvided data:\\n\\n${JSON.stringify(data, null, 2)}\\n\\nRequired JSON schema:\\n\\n${JSON.stringify(schema, null, 2)}`,\n      detail: { errors, schema },\n    });\n  }\n}\n\n/**\n * Converts a Zod schema into a JSON schema, utilizing an in-memory cache for known objects.\n * @param options Provide a json schema and/or zod schema. JSON schema has priority.\n * @returns A JSON schema.\n */\nexport function toJsonSchema({\n  jsonSchema,\n  schema,\n}: ProvidedSchema): JSONSchema | undefined {\n  // if neither jsonSchema or schema is present return undefined\n  if (!jsonSchema && !schema) return null;\n  if (jsonSchema) return jsonSchema;\n  if (jsonSchemas.has(schema!)) return jsonSchemas.get(schema!)!;\n  const outSchema = zodToJsonSchema(schema!, {\n    removeAdditionalStrategy: 'strict',\n  });\n  const annotatedSchema = applyAnnotations(schema!, outSchema as JSONSchema);\n  jsonSchemas.set(schema!, annotatedSchema);\n  return annotatedSchema;\n}\n\n/**\n * Recursively applies annotations to a JSON schema by walking the Zod tree\n * and matching it against the JSON schema structure.\n *\n * Note: This currently does not resolve JSON schema `$ref` nodes. Annotations\n * on recursive schemas (using `z.lazy`) may not be correctly applied to the\n * referenced definitions.\n */\nfunction applyAnnotations(schema: z.ZodTypeAny, json: any): any {\n  if (!json || typeof json !== 'object') return json;\n\n  const annotationsToApply: Record<string, any>[] = [];\n  let current = schema;\n\n  // Collect all annotations in the hierarchy (outer to inner)\n  while (current) {\n    const ann = schemaAnnotations.get(current);\n    if (ann) annotationsToApply.push(ann);\n\n    if (\n      current instanceof z.ZodOptional ||\n      current instanceof z.ZodNullable ||\n      current instanceof z.ZodDefault ||\n      current instanceof z.ZodEffects\n    ) {\n      current = (current as any)._def.innerType || (current as any)._def.schema;\n    } else {\n      break;\n    }\n  }\n\n  // Resolve annotations (outer-most last so it wins)\n  const resolvedAnnotations: Record<string, any> = {};\n  for (let i = annotationsToApply.length - 1; i >= 0; i--) {\n    Object.assign(resolvedAnnotations, annotationsToApply[i]);\n  }\n\n  for (const key in resolvedAnnotations) {\n    if (Object.prototype.hasOwnProperty.call(json, key)) {\n      console.warn(\n        `Annotation key \"${key}\" conflicts with existing JSON schema property and will be ignored.`\n      );\n      continue;\n    }\n    json[key] = resolvedAnnotations[key];\n  }\n\n  const inner = current;\n  if (inner instanceof z.ZodObject && json.properties) {\n    for (const key in inner.shape) {\n      if (json.properties[key]) {\n        applyAnnotations(inner.shape[key], json.properties[key]);\n      }\n    }\n  } else if (inner instanceof z.ZodArray && json.items) {\n    applyAnnotations(inner.element, json.items);\n  } else if (inner instanceof z.ZodUnion && json.anyOf) {\n    for (let i = 0; i < inner.options.length; i++) {\n      applyAnnotations(inner.options[i], json.anyOf[i]);\n    }\n  } else if (inner instanceof z.ZodIntersection && json.allOf) {\n    const schemas: z.ZodTypeAny[] = [];\n    const collect = (s: z.ZodTypeAny) => {\n      if (s instanceof z.ZodIntersection) {\n        collect(s._def.left);\n        collect(s._def.right);\n      } else {\n        schemas.push(s);\n      }\n    };\n    collect(inner);\n    if (schemas.length === json.allOf.length) {\n      for (let i = 0; i < schemas.length; i++) {\n        applyAnnotations(schemas[i], json.allOf[i]);\n      }\n    }\n  } else if (inner instanceof z.ZodRecord && json.additionalProperties) {\n    applyAnnotations(inner.valueSchema, json.additionalProperties);\n  } else if (inner instanceof z.ZodTuple && Array.isArray(json.items)) {\n    for (let i = 0; i < inner.items.length; i++) {\n      applyAnnotations(inner.items[i], json.items[i]);\n    }\n  } else if (inner instanceof z.ZodDiscriminatedUnion && json.anyOf) {\n    for (let i = 0; i < inner.options.length; i++) {\n      applyAnnotations(inner.options[i], json.anyOf[i]);\n    }\n  }\n\n  return json;\n}\n\n/**\n * Schema validation error details.\n */\nexport interface ValidationErrorDetail {\n  path: string;\n  message: string;\n}\n\nfunction ajvErrorToValidationErrorDetail(\n  error: ErrorObject\n): ValidationErrorDetail {\n  return {\n    path: error.instancePath.substring(1).replace(/\\//g, '.') || '(root)',\n    message: error.message!,\n  };\n}\n\nfunction cfWorkerErrorToValidationErrorDetail(error: {\n  instanceLocation: string;\n  error: string;\n}): ValidationErrorDetail {\n  const path = error.instanceLocation.startsWith('#/')\n    ? error.instanceLocation.substring(2)\n    : '';\n  return {\n    path: path.replace(/\\//g, '.') || '(root)',\n    message: error.error,\n  };\n}\n\n/**\n * Validation response.\n */\nexport type ValidationResponse =\n  | {\n      valid: true;\n      errors?: undefined;\n      schema: JSONSchema;\n    }\n  | {\n      valid: false;\n      errors: ValidationErrorDetail[];\n      schema: JSONSchema;\n    };\n\n/**\n * Validates the provided data against the provided schema.\n */\nexport function validateSchema(\n  data: unknown,\n  options: ProvidedSchema\n): ValidationResponse {\n  const toValidate = toJsonSchema(options);\n  if (!toValidate) {\n    return { valid: true, schema: toValidate };\n  }\n  const validationMode = getGenkitRuntimeConfig().jsonSchemaMode;\n\n  if (validationMode === 'interpret') {\n    let validator = cfWorkerValidators.get(toValidate);\n    if (!validator) {\n      validator = new Validator(toValidate);\n      cfWorkerValidators.set(toValidate, validator);\n    }\n\n    const result = validator.validate(sanitizeForJsonSchema(data));\n    if (!result.valid) {\n      return {\n        valid: false,\n        errors: result.errors.map(cfWorkerErrorToValidationErrorDetail),\n        schema: toValidate,\n      };\n    }\n\n    return {\n      valid: result.valid,\n      schema: toValidate,\n    };\n  }\n\n  let validator = ajvValidators.get(toValidate);\n  if (!validator) {\n    validator = ajv.compile(toValidate);\n    ajvValidators.set(toValidate, validator);\n  }\n\n  const valid = validator(data) as boolean;\n  if (!valid) {\n    return {\n      valid: false,\n      errors: (validator.errors ?? []).map(ajvErrorToValidationErrorDetail),\n      schema: toValidate,\n    };\n  }\n\n  return { valid, schema: toValidate };\n}\n\n/**\n * Parses raw data object against the provided schema.\n */\nexport function parseSchema<T = unknown>(\n  data: unknown,\n  options: ProvidedSchema\n): T {\n  const result = validateSchema(data, options);\n  if (!result.valid) {\n    throw new ValidationError({\n      data,\n      errors: result.errors,\n      schema: result.schema,\n    });\n  }\n  return data as T;\n}\n\n/**\n * Registers provided schema as a named schema object in the Genkit registry.\n *\n * @hidden\n */\nexport function defineSchema<T extends z.ZodTypeAny>(\n  registry: Registry,\n  name: string,\n  schema: T\n): T {\n  registry.registerSchema(name, { schema });\n  return schema;\n}\n\n/**\n * Registers provided JSON schema as a named schema object in the Genkit registry.\n *\n * @hidden\n */\nexport function defineJsonSchema(\n  registry: Registry,\n  name: string,\n  jsonSchema: JSONSchema\n) {\n  registry.registerSchema(name, { jsonSchema });\n  return jsonSchema;\n}\n\nfunction sanitizeForJsonSchema(data: any): any {\n  if (Array.isArray(data)) {\n    return data.map(sanitizeForJsonSchema);\n  } else if (data !== null && typeof data === 'object') {\n    const out: any = {};\n    for (const key in data) {\n      if (data[key] !== undefined) {\n        out[key] = sanitizeForJsonSchema(data[key]);\n      }\n    }\n    return out;\n  }\n  return data;\n}\n"],"mappings":"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","names":["validator"]}