@toolsycc/json-schema-gen
Version:
Generate lightweight JSON Schema objects compatible with OpenAI Function Calling and JSON Mode.
133 lines (132 loc) • 4.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSchema = validateSchema;
exports.generateSchema = generateSchema;
exports.generateSchemaFromData = generateSchemaFromData;
function validateSchema(schema, data) {
const errors = [];
if (schema.type !== "object" || typeof data !== "object" || data === null || Array.isArray(data)) {
return { valid: false, errors: ["Root must be an object"] };
}
const props = schema.properties || {};
const required = schema.required || [];
for (const key of required) {
if (!(key in data)) {
errors.push(`Missing required field: "${key}"`);
}
}
for (const [key, definition] of Object.entries(props)) {
const value = data[key];
if (value === undefined)
continue;
switch (definition.type) {
case "string":
if (typeof value !== "string")
errors.push(`"${key}" should be a string`);
break;
case "number":
if (typeof value !== "number")
errors.push(`"${key}" should be a number`);
break;
case "integer":
if (!Number.isInteger(value))
errors.push(`"${key}" should be an integer`);
break;
case "boolean":
if (typeof value !== "boolean")
errors.push(`"${key}" should be a boolean`);
break;
case "object":
if (typeof value !== "object" || value === null || Array.isArray(value)) {
errors.push(`"${key}" should be an object`);
}
else {
const nested = validateSchema(definition, value);
nested.errors.forEach(e => errors.push(`${key}.${e}`));
}
break;
case "array":
if (!Array.isArray(value)) {
errors.push(`"${key}" should be an array`);
}
else if (definition.items) {
value.forEach((item, index) => {
const nested = validateSchema({
type: "object",
properties: { item: definition.items },
required: ["item"]
}, { item });
nested.errors.forEach(e => errors.push(`${key}[${index}].${e}`));
});
}
break;
case "null":
if (value !== null)
errors.push(`"${key}" should be null`);
break;
default:
errors.push(`"${key}" has unsupported type: ${definition.type}`);
}
}
return {
valid: errors.length === 0,
errors
};
}
function generateSchema(fields) {
const properties = {};
const required = [];
for (const [key, config] of Object.entries(fields)) {
const { required: isRequired, ...rest } = config;
properties[key] = rest;
if (isRequired)
required.push(key);
}
return {
type: "object",
properties,
...(required.length > 0 ? { required } : {})
};
}
function generateSchemaFromData(data) {
const detectType = (value) => {
if (Array.isArray(value)) {
return {
type: "array",
items: value.length > 0 ? detectType(value[0]) : {}
};
}
else if (value === null) {
return { type: "null" };
}
else if (typeof value === "object") {
return generateSchemaFromData(value);
}
else if (typeof value === "number") {
return Number.isInteger(value) ? { type: "integer" } : { type: "number" };
}
else if (typeof value === "boolean") {
return { type: "boolean" };
}
else if (typeof value === "string") {
return { type: "string" };
}
else {
return { type: "string" };
}
};
if (typeof data !== "object" || data === null || Array.isArray(data)) {
throw new Error("Input must be a plain object");
}
const properties = {};
const required = [];
for (const [key, value] of Object.entries(data)) {
properties[key] = detectType(value);
required.push(key);
}
return {
type: "object",
properties,
required
};
}