hey-api-builders
Version:
A custom plugin for @hey-api/openapi-ts that generates mock data builders with a lightweight custom runtime, Zod integration, or static mock generation.
312 lines • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.irToSchema = irToSchema;
exports.normalizeSchema = normalizeSchema;
exports.sanitizeSchema = sanitizeSchema;
exports.collectSchemas = collectSchemas;
const schema_validators_1 = require("./schema-validators");
const string_utils_1 = require("./string-utils");
/**
* Converts IR schema to JSON Schema format
* @param ir - IR schema object
* @param all - All schemas for reference resolution
* @param seen - Set of already processed schemas to avoid cycles
* @returns JSON Schema
*/
function irToSchema(ir, all, seen) {
const seenSet = seen || new Set();
if (!ir || typeof ir !== 'object') {
return {};
}
if (seenSet.has(ir)) {
return {};
}
seenSet.add(ir);
if (ir.$ref) {
const name = ir.$ref.replace('#/components/schemas/', '');
const target = all[name];
return target ? irToSchema(target, all, seenSet) : {};
}
if ((0, schema_validators_1.isEnum)(ir)) {
const enumIr = ir;
if (Array.isArray(enumIr.enum)) {
const enumValues = enumIr.enum;
const anyOfSchemas = enumValues.map((value) => ({ const: value }));
return { anyOf: anyOfSchemas };
}
if (enumIr.type === 'enum' && Array.isArray(enumIr.items)) {
const enumValues = enumIr.items
.filter((item) => item && typeof item === 'object' && 'const' in item)
.map((item) => item.const);
if (enumValues.length > 0) {
const anyOfSchemas = enumValues.map((value) => ({ const: value }));
return { anyOf: anyOfSchemas };
}
}
if (!enumIr.enum && Array.isArray(enumIr.items)) {
const items = enumIr.items;
if (items.length > 0 &&
items.every((it) => it && typeof it === 'object' && 'const' in it)) {
const enumValues = items.map((it) => it.const);
const anyOfSchemas = enumValues.map((value) => ({ const: value }));
return { anyOf: anyOfSchemas };
}
}
}
const out = {};
if (ir.type) {
out.type = ir.type;
}
else if (ir.properties) {
out.type = 'object';
}
else if (ir.items) {
out.type = 'array';
}
const extendedIr = ir;
if (extendedIr.nullable) {
if (typeof out.type === 'string') {
out.type = [out.type, 'null'];
}
else if (Array.isArray(out.type) && !out.type.includes('null')) {
out.type.push('null');
}
}
if (ir.properties) {
out.properties = {};
for (const [k, v] of Object.entries(ir.properties)) {
out.properties[k] = irToSchema(v, all, seenSet);
}
}
if (ir.required && ir.required.length) {
out.required = [...ir.required];
}
if (typeof ir.additionalProperties !== 'undefined') {
if (typeof ir.additionalProperties === 'boolean') {
out.additionalProperties = ir.additionalProperties;
}
else {
out.additionalProperties = irToSchema(ir.additionalProperties, all, seenSet);
}
}
if (ir.items) {
if (Array.isArray(ir.items)) {
out.items = ir.items.map((i) => irToSchema(i, all, seenSet));
}
else {
out.items = irToSchema(ir.items, all, seenSet);
}
}
const extendedIrWithComposition = ir;
if (extendedIrWithComposition.allOf) {
out.allOf = extendedIrWithComposition.allOf.map((s) => irToSchema(s, all, seenSet));
}
if (extendedIrWithComposition.anyOf) {
out.anyOf = extendedIrWithComposition.anyOf.map((s) => irToSchema(s, all, seenSet));
}
if (extendedIrWithComposition.oneOf) {
out.oneOf = extendedIrWithComposition.oneOf.map((s) => irToSchema(s, all, seenSet));
}
const copy = [
'format',
'pattern',
'minimum',
'maximum',
'exclusiveMinimum',
'exclusiveMaximum',
'minLength',
'maxLength',
'minItems',
'maxItems',
'uniqueItems',
'minProperties',
'maxProperties',
'title',
'description',
'default',
'deprecated',
'readOnly',
'writeOnly',
'example',
];
for (const k of copy) {
const val = ir[k];
if (typeof val !== 'undefined') {
if (k === 'example') {
out.examples = [val];
}
else {
out[k] = val;
}
}
}
return out;
}
/**
* Normalizes a schema node for consistent processing
* @param node - Schema node to normalize
* @returns Normalized schema node
*/
function normalizeSchema(node) {
if (!node || typeof node !== 'object') {
return node;
}
const workingNode = { ...node };
if (workingNode.type === 'enum') {
const enumValues = [];
if (Array.isArray(workingNode.items)) {
for (const item of workingNode.items) {
if (item && typeof item === 'object' && 'const' in item) {
enumValues.push(item.const);
}
}
}
if (enumValues.length > 0) {
const primitiveTypes = new Set(enumValues.map((v) => (v === null ? 'null' : typeof v)));
let inferred = primitiveTypes.size === 1
? [...primitiveTypes][0]
: 'string';
if (inferred === 'number') {
if (enumValues.every((v) => typeof v === 'number' && Number.isInteger(v))) {
inferred = 'integer';
}
}
if (inferred === 'object') {
inferred = 'string';
}
workingNode.type = inferred;
workingNode.enum = enumValues;
}
else {
workingNode.type = 'string';
}
delete workingNode.items;
delete workingNode.logicalOperator;
}
if (Array.isArray(workingNode.items)) {
const hasEnumItems = workingNode.items.some((item) => item && typeof item === 'object' && item.type === 'enum');
if (hasEnumItems) {
workingNode.items = workingNode.items.map((item) => {
if (item && typeof item === 'object' && item.type === 'enum') {
return normalizeSchema(item);
}
return item;
});
}
}
if (workingNode.type === 'array' &&
Array.isArray(workingNode.items) &&
workingNode.items.length > 0 &&
workingNode.items.every((item) => item &&
typeof item === 'object' &&
'type' in item &&
(item.type === 'string' ||
item.type === 'null' ||
item.type === 'number' ||
item.type === 'integer' ||
item.type === 'boolean' ||
item.type === 'object'))) {
workingNode.anyOf = workingNode.items;
delete workingNode.type;
delete workingNode.items;
}
if (workingNode.properties && typeof workingNode.properties === 'object') {
for (const key of Object.keys(workingNode.properties)) {
const prop = workingNode.properties[key];
if (prop !== undefined) {
workingNode.properties[key] = normalizeSchema(prop);
}
}
}
if (workingNode.items) {
if (Array.isArray(workingNode.items)) {
workingNode.items = workingNode.items.map((it) => normalizeSchema(it));
}
else {
workingNode.items = normalizeSchema(workingNode.items);
}
}
for (const k of ['allOf', 'anyOf', 'oneOf']) {
const schemaArray = workingNode[k];
if (Array.isArray(schemaArray)) {
workingNode[k] = schemaArray.map((s) => normalizeSchema(s));
}
}
return workingNode;
}
/**
* Sanitizes a schema by removing invalid properties
* @param node - Schema node to sanitize
* @returns Sanitized schema
*/
function sanitizeSchema(node) {
if (!node || typeof node !== 'object') {
return node;
}
const workingNode = { ...node };
if (workingNode.type === 'enum') {
delete workingNode.type;
if (Array.isArray(workingNode.enum)) {
workingNode.type = 'string';
}
}
if (workingNode.type === 'unknown') {
delete workingNode.type;
}
if (!workingNode.type && Array.isArray(workingNode.enum)) {
workingNode.type = 'string';
}
delete workingNode.logicalOperator;
if (workingNode.properties && typeof workingNode.properties === 'object') {
const properties = workingNode.properties;
for (const k of Object.keys(properties)) {
const prop = properties[k];
if (prop !== undefined) {
properties[k] = sanitizeSchema(prop);
}
}
}
if (workingNode.additionalProperties && typeof workingNode.additionalProperties === 'object') {
workingNode.additionalProperties = sanitizeSchema(workingNode.additionalProperties);
}
if (workingNode.items) {
if (Array.isArray(workingNode.items)) {
workingNode.items = workingNode.items.map(sanitizeSchema);
}
else {
workingNode.items = sanitizeSchema(workingNode.items);
}
}
['allOf', 'anyOf', 'oneOf'].forEach((k) => {
const schemaArray = workingNode[k];
if (Array.isArray(schemaArray)) {
workingNode[k] = schemaArray.map(sanitizeSchema);
}
});
return workingNode;
}
/**
* Collects and processes all schemas from IR
* @param all - All IR schemas
* @returns Array of schema metadata
*/
function collectSchemas(all, normalize) {
const metas = [];
for (const [name, irSchema] of Object.entries(all)) {
let typeName = name.replace(/Schema$/, '');
typeName = normalize ? normalize(typeName) : (0, string_utils_1.toPascal)((0, string_utils_1.normalizeTypeName)(typeName));
const jsf = sanitizeSchema(normalizeSchema(irToSchema(irSchema, all)));
const schemaWithType = jsf;
const t = schemaWithType.type;
const isObject = (0, schema_validators_1.isObjectType)(t);
metas.push({
typeName,
constName: `${(0, string_utils_1.safeTypeName)(typeName)}Schema`,
isEnum: (0, schema_validators_1.isEnum)(irSchema),
schema: jsf,
isObject,
});
}
return metas;
}
//# sourceMappingURL=schema-transformer.js.map