UNPKG

@horizon-backend/domain-data-core

Version:

Core domain data utilities for Horizon Platform - Schema generators, data enrichers, converters and specifications

413 lines (405 loc) 13.4 kB
'use strict'; var dateFns = require('date-fns'); var locale = require('date-fns/locale'); var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/json-schema-to-zod-generator/index.ts var json_schema_to_zod_generator_exports = {}; __export(json_schema_to_zod_generator_exports, { JsonToZodGenerator: () => JsonToZodGenerator }); var JsonToZodGenerator = class { /** * Aplica inferência inteligente em um campo */ static applyInference(field, options) { const inferredField = { ...field }; if (options.logInference) { this.inferenceLog = []; } if (field.validation?.precision && options.enablePrecisionValidation) { const multipleOf = Math.pow(10, -field.validation.precision); this.log(`\u{1F522} ${field.key}: precision ${field.validation.precision} \u2192 multipleOf ${multipleOf}`); } if (field.mask) { this.log(`\u{1F3AD} ${field.key}: mask "${field.mask}" \u2192 FRONTEND ONLY (backend receives clean data)`); } if (field.type === "Json") { this.log(`\u{1F4E6} ${field.key}: Json type \u2192 z.any() or z.record()`); } if (field.type === "String[]" && field.searchable) { this.log(`\u{1F50D} ${field.key}: String[] + searchable \u2192 GIN index (DB only)`); } if (field.type === "String" && field.searchable && !field.validation?.maxLength) { this.log(`\u{1F4DD} ${field.key}: String + searchable + no maxLength \u2192 fulltext index (DB only)`); } return inferredField; } static log(message) { this.inferenceLog.push(message); } /** * Converte um campo para Zod */ static fieldToZod(field, options) { const processedField = this.applyInference(field, options); const { key, type, label, validation, enum: enumValues } = processedField; let zodType = ""; let validations = []; switch (type) { case "String": if (enumValues && Object.keys(enumValues).length > 0) { const enumArray = Object.keys(enumValues).map((k) => `"${k}"`).join(", "); zodType = `z.enum([${enumArray}])`; } else { zodType = "z.string()"; } if (validation?.minLength) validations.push(`.min(${validation.minLength})`); if (validation?.maxLength) validations.push(`.max(${validation.maxLength})`); break; case "Number": zodType = "z.number()"; if (validation?.min !== void 0) validations.push(`.min(${validation.min})`); if (validation?.max !== void 0) validations.push(`.max(${validation.max})`); if (validation?.precision && options.enablePrecisionValidation) { const multipleOf = Math.pow(10, -validation.precision); validations.push(`.multipleOf(${multipleOf})`); } break; case "Boolean": zodType = "z.boolean()"; break; case "String[]": if (enumValues && Object.keys(enumValues).length > 0) { const enumArray = Object.keys(enumValues).map((k) => `"${k}"`).join(", "); zodType = `z.array(z.enum([${enumArray}]))`; } else { zodType = "z.array(z.string())"; } break; case "Array": zodType = "z.array(z.any())"; break; case "Json": zodType = "z.any()"; break; case "Json[]": zodType = "z.array(z.any())"; break; default: zodType = "z.any()"; } zodType += validations.join(""); if (options.addDescriptions && label) { zodType += `.describe("${label}")`; } if (!validation?.required) { zodType += ".optional()"; } return ` ${key}: ${zodType},`; } /** * Gera o schema Zod completo */ static generate(fields, options) { const { schemaName, sortFields = false, exportType = true, logInference = false } = options; if (logInference) { console.log(` \u{1F9E0} INICIANDO GERA\xC7\xC3O COM INFER\xCANCIA - ${schemaName}`); console.log(`\u{1F4CA} Total de campos: ${fields.length}`); } const processedFields = sortFields ? [...fields].sort((a, b) => a.key.localeCompare(b.key)) : fields; const imports = `import { z } from "zod" `; const schemaFields = processedFields.map((field) => this.fieldToZod(field, options)).join("\n"); const schema = ` // Schema Horizon v2.2.0 para ${schemaName} // Gerado automaticamente com infer\xEAncia inteligente export const ${schemaName}Zod = z.object({ ${schemaFields} }) `; const typeExport = exportType ? ` // Tipo inferido a partir do schema export type ${schemaName}Type = z.infer<typeof ${schemaName}Zod> // Fun\xE7\xE3o helper para valida\xE7\xE3o export const validate${schemaName} = (data: unknown): ${schemaName}Type => { return ${schemaName}Zod.parse(data) } // Fun\xE7\xE3o helper para valida\xE7\xE3o safe export const safeValidate${schemaName} = (data: unknown) => { return ${schemaName}Zod.safeParse(data) } // Fun\xE7\xE3o helper para valida\xE7\xE3o parcial export const validatePartial${schemaName} = (data: unknown) => { return ${schemaName}Zod.partial().parse(data) } ` : ""; if (logInference && this.inferenceLog.length > 0) { console.log(` \u{1F9E0} INFER\xCANCIAS APLICADAS:`); this.inferenceLog.forEach((log) => console.log(log)); console.log(` \u2705 Gera\xE7\xE3o conclu\xEDda com ${this.inferenceLog.length} infer\xEAncias`); } return imports + schema + typeExport; } /** * Gera schema a partir de arquivo JSON v2.2.0 */ static async generateFromFile(jsonPath, options) { const fs = await import('fs/promises'); const jsonContent = await fs.readFile(jsonPath, "utf-8"); const data = JSON.parse(jsonContent); const fields = data.fields || data; if (!Array.isArray(fields)) { throw new Error('JSON deve conter um array de campos ou objeto com propriedade "fields"'); } return this.generate(fields, options); } /** * Salva o schema gerado */ static async saveToFile(fields, options, outputPath) { const fs = await import('fs/promises'); const path = await import('path'); const generated = this.generate(fields, options); const dir = path.dirname(outputPath); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(outputPath, generated, "utf-8"); if (options.logInference) { console.log(` \u{1F4BE} Schema salvo: ${outputPath}`); } } /** * Analisa um schema e retorna estatísticas */ static analyzeSchema(fields) { const analysis = { totalFields: fields.length, fieldTypes: {}, fieldFormats: {}, fieldCategories: {}, requiredFields: 0, fieldsWithValidation: 0, fieldsWithMask: 0, fieldsWithEnum: 0, fieldsWithConditions: 0, fieldsWithParent: 0 }; fields.forEach((field) => { analysis.fieldTypes[field.type] = (analysis.fieldTypes[field.type] || 0) + 1; if (field.format) { analysis.fieldFormats[field.format] = (analysis.fieldFormats[field.format] || 0) + 1; } if (field.categories) { field.categories.forEach((cat) => { analysis.fieldCategories[cat] = (analysis.fieldCategories[cat] || 0) + 1; }); } if (field.validation?.required) analysis.requiredFields++; if (field.validation) analysis.fieldsWithValidation++; if (field.mask) analysis.fieldsWithMask++; if (field.enum) analysis.fieldsWithEnum++; if (field.conditions) analysis.fieldsWithConditions++; if (field.parent) analysis.fieldsWithParent++; }); return analysis; } }; JsonToZodGenerator.inferenceLog = []; // src/domain-data-display-enricher/index.ts var domain_data_display_enricher_exports = {}; __export(domain_data_display_enricher_exports, { EnrichFieldsWithMetadata: () => EnrichFieldsWithMetadata, default: () => domain_data_display_enricher_default, enrichDomainDataForDisplay: () => enrichDomainDataForDisplay, formatters: () => formatters, templateProcessor: () => templateProcessor }); function formatCurrency(value, currency = "BRL", locale = "pt-BR") { if (value === null || value === void 0) { return "Valor sob consulta"; } const numValue = Number(value); if (isNaN(numValue)) return String(value); return numValue.toLocaleString(locale, { style: "currency", currency, minimumFractionDigits: 2 }); } function formatDateTime(value, locale$1 = "pt-BR") { try { const date = value instanceof Date ? value : new Date(value); if (isNaN(date.getTime())) return String(value); if (locale$1 === "pt-BR") { return dateFns.format(date, "d 'de' MMMM 'de' yyyy", { locale: locale.ptBR }); } return date.toLocaleDateString(locale$1); } catch { return String(value); } } function formatArea(value, unit = "m\xB2") { if (value === null || value === void 0) return ""; const unitMap = { "m2": "m\xB2", "m\xB2": "m\xB2", "ft2": "ft\xB2", "hectare": "hectares", "hectares": "hectares", "km2": "km\xB2" }; const displayUnit = unitMap[unit] || unit; return `${value} ${displayUnit}`; } function formatDistance(value, unit = "m") { if (value === null || value === void 0) return ""; const unitMap = { "m": "m", "meters": "m", "km": "km", "mi": "mi", "miles": "mi" }; const displayUnit = unitMap[unit] || unit; return `${value}${displayUnit}`; } function formatPercent(value) { if (value === null || value === void 0) return ""; const numValue = Number(value); if (isNaN(numValue)) return String(value); if (numValue > 1) { return `${numValue}%`; } return `${(numValue * 100).toFixed(2)}%`; } function formatCount(value) { if (value === null || value === void 0) return "0"; return String(Math.floor(Number(value))); } function formatYear(value) { if (value === null || value === void 0) return ""; return String(value); } function processTemplate(template, value, valueLabel) { if (!template) return void 0; let result = template; result = result.replace(/{{value}}/g, String(value)); result = result.replace(/{{valueLabel}}/g, valueLabel); result = result.replace(/{{p:(.*?)}}/g, (_, plural) => { const numValue = Number(value); return numValue !== 1 ? plural : ""; }); result = result.replace(/{{s:(.*?)}}/g, (_, singular) => { const numValue = Number(value); return numValue === 1 ? singular : ""; }); return result; } function generateValueLabel(field, options = {}) { const { value, format: format2, unit, type } = field; const { locale = "pt-BR", currency = "BRL" } = options; if (value === null || value === void 0) { if (format2 === "currency") return "Valor sob consulta"; return ""; } if (field.valueLabel) { return field.valueLabel; } switch (format2) { case "currency": return formatCurrency(value, unit || currency, locale); case "date": case "datetime": return formatDateTime(value, locale); case "area": return formatArea(value, unit); case "distance": return formatDistance(value, unit); case "percent": return formatPercent(value); case "count": return formatCount(value); case "year": return formatYear(value); default: if (type === "Boolean") { return value ? "Sim" : "N\xE3o"; } if (type === "Json" || type === "Array" || type === "Json[]") { return JSON.stringify(value); } return String(value); } } function enrichField(fieldValue, metadata, options = {}) { const enrichedField = { value: fieldValue, ...metadata // Espalha todos os metadados }; const valueLabel = generateValueLabel(enrichedField, options); if (valueLabel) { enrichedField.valueLabel = valueLabel; } if (metadata.composedLabel) { const processed = processTemplate(metadata.composedLabel, fieldValue, valueLabel); if (processed) { enrichedField.composedLabel = processed; } } if (metadata.iconName && options.getIcon) { const icon = options.getIcon(metadata.iconName); if (icon) { enrichedField.icon = icon; } } return enrichedField; } function enrichDomainDataForDisplay(options) { const { data, metadata, ...enrichOptions } = options; const enrichedData = { ...data }; metadata.forEach((fieldMetadata) => { const fieldKey = fieldMetadata.key; if (fieldKey in enrichedData) { const fieldValue = enrichedData[fieldKey]; if (fieldValue === void 0) { return; } enrichedData[fieldKey] = enrichField(fieldValue, fieldMetadata, enrichOptions); } }); return enrichedData; } var formatters = { currency: formatCurrency, date: formatDateTime, area: formatArea, distance: formatDistance, percent: formatPercent, count: formatCount, year: formatYear }; var templateProcessor = processTemplate; function EnrichFieldsWithMetadata({ data, metadata }) { return enrichDomainDataForDisplay({ data, metadata }); } var domain_data_display_enricher_default = enrichDomainDataForDisplay; exports.domainDataDisplayEnricher = domain_data_display_enricher_exports; exports.jsonSchemaToZodGenerator = json_schema_to_zod_generator_exports; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map