UNPKG

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
"use strict"; 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