@mastra/schema-compat
Version:
Tool schema compatibility layer for Mastra.ai
258 lines (257 loc) • 10 kB
JavaScript
import { patchRecordSchemas } from "./zod-to-json.js";
import { toStandardSchema as toStandardSchema$2 } from "./standard-schema/adapters/ai-sdk.js";
import { n as toStandardSchema$3 } from "./json-schema-BstKYbJi.js";
import { toStandardSchema as toStandardSchema$4 } from "./standard-schema/adapters/zod-v3.js";
import z3 from "zod/v3";
import { toJSONSchema } from "zod/v4";
//#region src/standard-schema/adapters/zod-v4.ts
/**
* Supported JSON Schema targets for z.toJSONSchema().
* Works with both real Zod v4 and Zod 3.25's v4 compat layer.
*/
const SUPPORTED_TARGETS = /* @__PURE__ */ new Set([
"draft-07",
"draft-04",
"draft-2020-12"
]);
/**
* Maps Mastra's target names to Zod v4's expected format.
* Zod v4's z.toJSONSchema() expects "draft-7" instead of "draft-07",
* and "draft-4" instead of "draft-04".
*/
const ZOD_V4_TARGET_MAP = {
"draft-07": "draft-7",
"draft-04": "draft-4"
};
/**
* Converts a Zod v4 schema to JSON Schema using z.toJSONSchema().
*
* Works with both real Zod v4 and Zod 3.25's v4 compat layer.
*
* @internal
*/
function convertToJsonSchema(zodSchema, options, adapterOptions) {
patchRecordSchemas(zodSchema);
const target = SUPPORTED_TARGETS.has(options.target) ? options.target : "draft-07";
const jsonSchemaOptions = { target: ZOD_V4_TARGET_MAP[target] ?? target };
if (adapterOptions.unrepresentable) jsonSchemaOptions.unrepresentable = adapterOptions.unrepresentable;
if (adapterOptions.override) jsonSchemaOptions.override = adapterOptions.override;
return toJSONSchema(zodSchema, jsonSchemaOptions);
}
/**
* Wraps a Zod v4 schema to implement the full @standard-schema/spec interface.
*
* Zod v4 schemas (and Zod 3.25's v4 compat layer) implement `StandardSchemaV1`
* (validation) but may not implement `StandardJSONSchemaV1` (JSON Schema conversion)
* on the `~standard` property. This adapter adds the `jsonSchema` property using
* `z.toJSONSchema()` to provide JSON Schema conversion capabilities.
*
* @param zodSchema - A Zod v4 schema (has `_zod` property)
* @param adapterOptions - Options passed to z.toJSONSchema()
* @returns The schema wrapped with StandardSchemaWithJSON support
*/
function toStandardSchema$1(zodSchema, adapterOptions = {}) {
const wrapper = Object.create(zodSchema);
const existingStandard = zodSchema["~standard"];
const jsonSchemaConverter = {
input: (options) => {
return convertToJsonSchema(zodSchema, options, adapterOptions);
},
output: (options) => {
return convertToJsonSchema(zodSchema, options, adapterOptions);
}
};
Object.defineProperty(wrapper, "~standard", {
value: {
...existingStandard,
jsonSchema: jsonSchemaConverter
},
writable: false,
enumerable: true,
configurable: false
});
return wrapper;
}
//#endregion
//#region src/standard-schema/standard-schema.ts
/**
* Override function for JSON Schema conversion.
* Handles types that Zod v4 cannot natively represent in JSON Schema:
* - z.date() -> { type: 'string', format: 'date-time' }
*/
function jsonSchemaOverride(ctx) {
const zodSchema = ctx.zodSchema;
if (ctx.jsonSchema.type === "object" && ctx.jsonSchema.properties !== void 0 && !ctx.jsonSchema.additionalProperties) ctx.jsonSchema.additionalProperties = false;
if (zodSchema) {
if (zodSchema?.type === "date" || zodSchema?._def?.typeName === "ZodDate") {
if (zodSchema?.type === "date") {
ctx.jsonSchema.type = "string";
ctx.jsonSchema.format = "date-time";
}
ctx.jsonSchema["x-date"] = !zodSchema._zod?.def?.coerce;
} else if (zodSchema?.type === "object" && zodSchema._zod?.def?.catchall?.type === "unknown") ctx.jsonSchema.additionalProperties = true;
}
}
/**
* Library options for JSON Schema conversion.
* - unrepresentable: 'any' allows z.custom() and other unrepresentable types to be converted to {}
* instead of throwing "Custom types cannot be represented in JSON Schema"
* - override: converts z.date() to { type: 'string', format: 'date-time' }
*/
const JSON_SCHEMA_LIBRARY_OPTIONS = {
unrepresentable: "any",
override: jsonSchemaOverride
};
function isVercelSchema(schema) {
return typeof schema === "object" && schema !== null && "_type" in schema && "jsonSchema" in schema && typeof schema.jsonSchema === "object";
}
/**
* Check if a schema is Zod v4 (has _zod property which is v4-only)
*/
function isZodV4(schema) {
return typeof schema === "object" && schema !== null && "_zod" in schema;
}
/**
* Check if a schema is Zod v3.
*
* Zod v3 can come from:
* 1. The old standalone 'zod-v3' package
* 2. The 'zod/v3' compat export from modern zod
*
* We detect Zod v3 by checking:
* - Has ~standard.vendor === 'zod' (both v3 and v4 have this)
* - Does NOT have ~standard.jsonSchema (only Zod v4 has native JSON Schema support)
* - Does NOT have _zod property (only Zod v4 has this)
*
* Note: We can't use instanceof z3.ZodType because the old 'zod-v3' package
* has a different prototype chain than 'zod/v3'.
*/
function isZodV3(schema) {
if (schema === null || typeof schema !== "object") return false;
if (isZodV4(schema)) return false;
if ("~standard" in schema) {
const std = schema["~standard"];
if (typeof std === "object" && std !== null && std.vendor === "zod" && !("jsonSchema" in std)) return true;
}
return schema instanceof z3.ZodType;
}
function toStandardSchema(schema) {
if (isZodV4(schema)) patchRecordSchemas(schema);
if (isStandardSchemaWithJSON(schema)) return schema;
if (isZodV4(schema)) return toStandardSchema$1(schema, {
unrepresentable: JSON_SCHEMA_LIBRARY_OPTIONS.unrepresentable,
override: JSON_SCHEMA_LIBRARY_OPTIONS.override
});
if (isZodV3(schema)) return toStandardSchema$4(schema);
if (isVercelSchema(schema)) return toStandardSchema$2(schema);
if (schema === null || typeof schema !== "object" && typeof schema !== "function") throw new Error(`Unsupported schema type: ${typeof schema}`);
if (typeof schema === "function") throw new Error(`Unsupported schema type: function (schema libraries should implement StandardSchemaWithJSON)`);
return toStandardSchema$3(schema);
}
/**
* Type guard to check if a value implements the StandardSchemaV1 interface.
*
* @param value - The value to check
* @returns True if the value implements StandardSchemaV1
*
* @example
* ```typescript
* import { isStandardSchema } from '@mastra/schema-compat';
*
* if (isStandardSchema(someValue)) {
* const result = someValue['~standard'].validate(input);
* }
* ```
*/
function isStandardSchema(value) {
if (value === null || typeof value !== "object" && typeof value !== "function") return false;
if (!("~standard" in value)) return false;
const std = value["~standard"];
return typeof std === "object" && std !== null && "version" in std && std.version === 1 && "vendor" in std && "validate" in std && typeof std.validate === "function";
}
/**
* Type guard to check if a value implements the StandardJSONSchemaV1 interface.
*
* @param value - The value to check
* @returns True if the value implements StandardJSONSchemaV1
*
* @example
* ```typescript
* import { isStandardJSONSchema } from '@mastra/schema-compat';
*
* if (isStandardJSONSchema(someValue)) {
* const jsonSchema = someValue['~standard'].jsonSchema.output({ target: 'draft-07' });
* }
* ```
*/
function isStandardJSONSchema(value) {
if (value === null || typeof value !== "object" && typeof value !== "function") return false;
if (!("~standard" in value)) return false;
const std = value["~standard"];
if (typeof std !== "object" || std === null) return false;
if (!("version" in std) || std.version !== 1 || !("vendor" in std)) return false;
if (!("jsonSchema" in std) || typeof std.jsonSchema !== "object") return false;
return typeof std.jsonSchema.input === "function" && typeof std.jsonSchema.output === "function";
}
/**
* Type guard to check if a value implements both StandardSchemaV1 and StandardJSONSchemaV1.
*
* @param value - The value to check
* @returns True if the value implements both interfaces
*
* @example
* ```typescript
* import { isStandardSchemaWithJSON } from '@mastra/schema-compat';
*
* if (isStandardSchemaWithJSON(someValue)) {
* // Can use both validation and JSON Schema conversion
* const result = someValue['~standard'].validate(input);
* const jsonSchema = someValue['~standard'].jsonSchema.output({ target: 'draft-07' });
* }
* ```
*/
function isStandardSchemaWithJSON(value) {
return isStandardSchema(value) && isStandardJSONSchema(value);
}
/**
* Converts a StandardSchemaWithJSON to a JSON Schema.
*
* @param schema - The StandardSchemaWithJSON schema to convert
* @param options - Conversion options
* @param options.target - The JSON Schema target version (default: 'draft-07')
* @param options.io - Whether to use input or output schema (default: 'output')
* - 'input': Use for tool parameters, function arguments, request bodies
* - 'output': Use for return types, response bodies
* @returns The JSON Schema representation
*
* @example
* ```typescript
* import { standardSchemaToJSONSchema, toStandardSchema } from '@mastra/schema-compat';
* import { z } from 'zod';
*
* const zodSchema = z.object({ name: z.string() });
* const standardSchema = toStandardSchema(zodSchema);
*
* // For output types (default)
* const outputSchema = standardSchemaToJSONSchema(standardSchema);
*
* // For input types (tool parameters)
* const inputSchema = standardSchemaToJSONSchema(standardSchema, { io: 'input' });
* ```
*/
function standardSchemaToJSONSchema(schema, options = {}) {
const { target = "draft-07", io = "output", override = JSON_SCHEMA_LIBRARY_OPTIONS.override } = options;
const jsonSchemaFn = schema["~standard"].jsonSchema[io];
let jsonSchema = jsonSchemaFn({
target,
libraryOptions: {
...JSON_SCHEMA_LIBRARY_OPTIONS,
override
}
});
jsonSchema = JSON.parse(JSON.stringify(jsonSchema));
return jsonSchema;
}
//#endregion
export { standardSchemaToJSONSchema as a, isStandardSchemaWithJSON as i, isStandardJSONSchema as n, toStandardSchema as o, isStandardSchema as r, JSON_SCHEMA_LIBRARY_OPTIONS as t };
//# sourceMappingURL=schema-Bv9OdW1f.js.map