@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
175 lines (173 loc) • 7.49 kB
JavaScript
import { z as z$1 } from "zod";
//#region src/ai/tools/schema-to-type-string.ts
const toolTypeStringCache = /* @__PURE__ */ new WeakMap();
function getToolTypeStrings(tool) {
const cached = toolTypeStringCache.get(tool);
if (cached) return cached;
const types = { inputType: schemaToTypeString(z$1.toJSONSchema(tool.inputSchema, { io: "input" }), "Input") };
if (tool.output) types.outputType = schemaToTypeString(z$1.toJSONSchema(tool.output), "Output");
toolTypeStringCache.set(tool, types);
return types;
}
function schemaToTypeString(schema, rootName) {
const root = schema;
const rootReferenced = referencesRoot(root);
const context = createContext(root, rootName, rootReferenced);
const rootType = rootReferenced ? renderDeclaration(root, context, rootName) : renderSchema(root, context);
return [...renderDefs(context), rootType].join("\n\n");
}
function createContext(schema, rootName, rootDeclared) {
const context = {
defs: schema.$defs ?? {},
defNames: /* @__PURE__ */ new Map(),
rootName,
usedNames: new Set(rootDeclared ? [rootName] : [])
};
for (const key of Object.keys(context.defs).sort()) context.defNames.set(key, getAvailableName(getDefinitionName(key), context.usedNames));
return context;
}
function referencesRoot(schema) {
if (!schema || typeof schema === "boolean") return false;
if (schema.$ref === "#") return true;
return [
...Object.values(schema.$defs ?? {}),
...schema.anyOf ?? [],
...schema.oneOf ?? [],
...schema.allOf ?? [],
...Object.values(schema.properties ?? {}),
...getSchemaEntries(schema.items),
...schema.prefixItems ?? [],
...typeof schema.additionalProperties === "object" ? [schema.additionalProperties] : []
].some((child) => referencesRoot(child));
}
function getSchemaEntries(entries) {
if (!entries) return [];
return Array.isArray(entries) ? entries : [entries];
}
function getSchemaTypes(schema) {
if (!schema.type) return [];
return Array.isArray(schema.type) ? schema.type : [schema.type];
}
function renderDefs(context) {
return Object.keys(context.defs).sort().map((key) => renderDeclaration(context.defs[key], context, context.defNames.get(key)));
}
function renderDeclaration(schema, context, name) {
if (canRenderInterface(schema)) return `interface ${name} ${renderObject(schema, context)}`;
return `type ${name} = ${renderSchema(schema, context)};`;
}
function renderSchema(schema, context) {
if (schema === false) return "never";
if (!schema || schema === true) return "unknown";
if (schema.$ref) return renderRef(schema.$ref, context);
if ("const" in schema) return renderLiteral(schema.const);
if (schema.enum) return renderUnion(schema.enum.map(renderLiteral));
if (schema.anyOf) return renderUnion(schema.anyOf.map((entry) => renderSchema(entry, context)));
if (schema.oneOf) return renderUnion(schema.oneOf.map((entry) => renderSchema(entry, context)));
if (schema.allOf) return schema.allOf.map((entry) => parenthesizeUnion(renderSchema(entry, context))).join(" & ");
const types = getSchemaTypes(schema);
if (types.length > 1) return renderUnion(types.map((type) => renderSchema({
...schema,
type
}, context)));
switch (types[0]) {
case "object": return renderObject(schema, context);
case "array": return renderArray(schema, context);
case "string": return "string";
case "number":
case "integer": return "number";
case "boolean": return "boolean";
case "null": return "null";
default: return renderUnknownSchema(schema, context);
}
}
function canRenderInterface(schema) {
if (!schema || typeof schema === "boolean") return false;
if (schema.$ref || schema.anyOf || schema.oneOf || schema.allOf || "const" in schema || schema.enum) return false;
const types = getSchemaTypes(schema);
if (types.length > 1) return false;
const isObjectLike = types[0] === "object" || !!schema.properties;
const hasNamedProperties = Object.keys(schema.properties ?? {}).length > 0;
return isObjectLike && (hasNamedProperties || schema.additionalProperties === false);
}
function renderRef(ref, context) {
if (ref === "#") return context.rootName;
if (ref.startsWith("#/$defs/")) {
const key = decodeJsonPointerToken(decodeURIComponent(ref.slice(8)));
return context.defNames.get(key) ?? "unknown";
}
return "unknown";
}
function renderUnknownSchema(schema, context) {
if (schema.properties || schema.additionalProperties) return renderObject(schema, context);
if (schema.items || schema.prefixItems) return renderArray(schema, context);
return "unknown";
}
function renderObject(schema, context) {
const properties = schema.properties ?? {};
const required = new Set(schema.required ?? []);
const entries = Object.entries(properties);
const additional = schema.additionalProperties;
if (entries.length === 0 && additional && typeof additional === "object") return `Record<string, ${renderSchema(additional, context)}>`;
if (entries.length === 0) return additional === false ? "{}" : "Record<string, unknown>";
const lines = entries.flatMap(([key, property]) => {
const optional = required.has(key) ? "" : "?";
const description = renderDescription(property.description);
const member = `${formatPropertyKey(key)}${optional}: ${renderSchema(property, context)};`;
return description ? [description, member] : [member];
});
if (additional && typeof additional === "object") {
const additionalType = renderSchema(additional, context);
const propertyTypes = entries.map(([, property]) => renderSchema(property, context));
lines.push(`[key: string]: ${renderUnion([additionalType, ...propertyTypes])};`);
} else if (additional === true) lines.push("[key: string]: unknown;");
return `{\n${indent(lines.join("\n"))}\n}`;
}
function renderArray(schema, context) {
if (schema.prefixItems) return `[${schema.prefixItems.map((item) => renderSchema(item, context)).join(", ")}]`;
if (Array.isArray(schema.items)) return `[${schema.items.map((item) => renderSchema(item, context)).join(", ")}]`;
return `${parenthesizeUnion(renderSchema(schema.items, context))}[]`;
}
function renderUnion(types) {
const uniqueTypes = [...new Set(types)];
if (uniqueTypes.length === 0) return "never";
return uniqueTypes.join(" | ");
}
function renderLiteral(value) {
if (typeof value === "string") return JSON.stringify(value);
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
return "unknown";
}
function renderDescription(description) {
if (!description) return;
return `/** ${description.replaceAll("*/", "*\\/").trim()} */`;
}
function parenthesizeUnion(type) {
return type.includes(" | ") ? `(${type})` : type;
}
function indent(value) {
return value.split("\n").map((line) => `\t${line}`).join("\n");
}
function formatPropertyKey(key) {
return /^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key);
}
function getDefinitionName(key) {
const name = key.split(/[^A-Za-z0-9_$]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
if (!name) return "Schema";
if (/^\d/.test(name)) return `Schema${name}`;
return name;
}
function decodeJsonPointerToken(value) {
return value.replaceAll("~1", "/").replaceAll("~0", "~");
}
function getAvailableName(baseName, usedNames) {
let name = baseName;
let index = 1;
while (usedNames.has(name)) {
name = index === 1 ? `${baseName}Def` : `${baseName}Def${index}`;
index++;
}
usedNames.add(name);
return name;
}
//#endregion
export { getToolTypeStrings, schemaToTypeString };