@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
JavaScript
;
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