@semacode/cli 1.5.16 → 1.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +272 -0
- package/LICENSE +22 -0
- package/README.md +104 -112
- package/SEMA_BRIEF.curto.txt +9 -0
- package/SEMA_BRIEF.md +63 -0
- package/SEMA_BRIEF.micro.txt +7 -0
- package/SEMA_INDEX.json +783 -0
- package/dist/angular-consumer-standalone.d.ts +6 -0
- package/dist/angular-consumer-standalone.js.map +1 -0
- package/dist/cpp-symbols.d.ts +10 -0
- package/dist/cpp-symbols.js.map +1 -0
- package/dist/docs.d.ts +56 -0
- package/dist/docs.js.map +1 -0
- package/dist/dotnet-http.d.ts +23 -0
- package/dist/dotnet-http.js.map +1 -0
- package/dist/drift.d.ts +225 -0
- package/dist/drift.js +5 -111
- package/dist/drift.js.map +1 -0
- package/dist/go-http.d.ts +23 -0
- package/dist/go-http.js.map +1 -0
- package/dist/importador.d.ts +31 -0
- package/dist/importador.js +1 -200
- package/dist/importador.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +59 -479
- package/dist/index.js.map +1 -0
- package/dist/java-http.d.ts +23 -0
- package/dist/java-http.js.map +1 -0
- package/dist/lua-symbols.d.ts +10 -0
- package/dist/lua-symbols.js.map +1 -0
- package/dist/php-symbols.d.ts +24 -0
- package/dist/php-symbols.js.map +1 -0
- package/dist/projeto.d.ts +53 -0
- package/dist/projeto.js +0 -20
- package/dist/projeto.js.map +1 -0
- package/dist/python-http.d.ts +23 -0
- package/dist/python-http.js.map +1 -0
- package/dist/rust-http.d.ts +23 -0
- package/dist/rust-http.js.map +1 -0
- package/dist/tipos.d.ts +3 -0
- package/dist/tipos.js.map +1 -0
- package/dist/typescript-http.d.ts +35 -0
- package/dist/typescript-http.js.map +1 -0
- package/docs/AGENT_STARTER.md +103 -0
- package/docs/cli.md +106 -0
- package/docs/como-ensinar-a-sema-para-ia.md +149 -0
- package/docs/deploy.md +70 -0
- package/docs/documentacao.md +63 -0
- package/docs/env.md +56 -0
- package/docs/extensao-vscode.md +45 -0
- package/docs/fluxo-pratico-ia-sema.md +181 -0
- package/docs/instalacao-e-primeiro-uso.md +112 -0
- package/docs/integracao-com-ia.md +103 -0
- package/docs/mcp.md +51 -0
- package/docs/pagamento-ponta-a-ponta.md +165 -0
- package/docs/persistencia-vendor-first.md +145 -0
- package/docs/prompt-base-ia-sema.md +105 -0
- package/docs/rollback.md +47 -0
- package/docs/sintaxe.md +212 -0
- package/exemplos/agendamento.sema +105 -0
- package/exemplos/assinatura.sema +133 -0
- package/exemplos/auditoria.sema +89 -0
- package/exemplos/autenticacao.sema +125 -0
- package/exemplos/automacao.sema +107 -0
- package/exemplos/cadastro_usuario.sema +54 -0
- package/exemplos/calculadora.sema +78 -0
- package/exemplos/crud_simples.sema +89 -0
- package/exemplos/estoque.sema +127 -0
- package/exemplos/exportacao.sema +94 -0
- package/exemplos/fila.sema +130 -0
- package/exemplos/integracao_externa.sema +94 -0
- package/exemplos/multi_tenant.sema +140 -0
- package/exemplos/notificacao.sema +149 -0
- package/exemplos/operacao_estrategia.sema +633 -0
- package/exemplos/pagamento.sema +434 -0
- package/exemplos/pagamento_dominio.sema +35 -0
- package/exemplos/pedido.sema +255 -0
- package/exemplos/permissao.sema +121 -0
- package/exemplos/persistencia_vendor_first.sema +86 -0
- package/exemplos/relatorio.sema +93 -0
- package/exemplos/replica_analitica_erp.sema +160 -0
- package/exemplos/testes_embutidos.sema +45 -0
- package/exemplos/tratamento_erro.sema +157 -0
- package/exemplos/upload_arquivo.sema +93 -0
- package/exemplos/webhook.sema +94 -0
- package/llms-full.txt +34 -0
- package/llms.txt +17 -0
- package/node_modules/@sema/gerador-css/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-css/dist/index.js +592 -0
- package/node_modules/@sema/gerador-css/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-css/package.json +7 -0
- package/node_modules/@sema/gerador-dart/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-dart/dist/index.js +44 -0
- package/node_modules/@sema/gerador-dart/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-dart/package.json +7 -0
- package/node_modules/@sema/gerador-html/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-html/dist/index.js +163 -0
- package/node_modules/@sema/gerador-html/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-html/package.json +7 -0
- package/node_modules/@sema/gerador-javascript/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-javascript/dist/index.js +421 -0
- package/node_modules/@sema/gerador-javascript/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-javascript/package.json +7 -0
- package/node_modules/@sema/gerador-lua/dist/index.d.ts +3 -0
- package/node_modules/@sema/gerador-lua/dist/index.js +328 -0
- package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-lua/package.json +7 -0
- package/node_modules/@sema/gerador-python/dist/index.d.ts +6 -0
- package/node_modules/@sema/gerador-python/dist/index.js +729 -0
- package/node_modules/@sema/gerador-python/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-python/package.json +7 -0
- package/node_modules/@sema/gerador-typescript/dist/index.d.ts +6 -0
- package/node_modules/@sema/gerador-typescript/dist/index.js +793 -0
- package/node_modules/@sema/gerador-typescript/dist/index.js.map +1 -0
- package/node_modules/@sema/gerador-typescript/package.json +7 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +123 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.js +2 -0
- package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +21 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +12 -0
- package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +9 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.js +460 -0
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/index.d.ts +35 -0
- package/node_modules/@sema/nucleo/dist/index.js +96 -0
- package/node_modules/@sema/nucleo/dist/index.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +5 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +877 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +345 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.js +2 -0
- package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +7 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js +122 -0
- package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +8 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +61 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +9 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.js +767 -0
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.d.ts +39 -0
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js +294 -0
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +59 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +1681 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +106 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +475 -0
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +91 -0
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +258 -0
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +2 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.js +25 -0
- package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +1 -0
- package/node_modules/@sema/nucleo/package.json +7 -0
- package/node_modules/@sema/padroes/dist/index.d.ts +25 -0
- package/node_modules/@sema/padroes/dist/index.js +316 -0
- package/node_modules/@sema/padroes/dist/index.js.map +1 -0
- package/node_modules/@sema/padroes/package.json +7 -0
- package/package.json +32 -23
- package/semacode-cli-1.3.1.tgz +0 -0
- package/src/angular-consumer-standalone.ts +0 -312
- package/src/cpp-symbols.ts +0 -82
- package/src/docs.ts +0 -535
- package/src/dotnet-http.ts +0 -355
- package/src/drift.ts +0 -4933
- package/src/go-http.ts +0 -118
- package/src/importador.ts +0 -3891
- package/src/index.ts +0 -5641
- package/src/java-http.ts +0 -247
- package/src/lua-symbols.ts +0 -114
- package/src/php-symbols.ts +0 -462
- package/src/projeto.ts +0 -862
- package/src/python-http.ts +0 -258
- package/src/rust-http.ts +0 -125
- package/src/tipos.ts +0 -24
- package/src/typescript-http.ts +0 -1076
- package/tsconfig.json +0 -20
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { descreverEstruturaModulo, extrairTiposNomeados, mapearTipoParaPython, normalizarNomeModulo, normalizarNomeParaSimbolo, } from "@sema/padroes";
|
|
3
|
+
function mapearCampoParaPython(campo) {
|
|
4
|
+
let anotacao;
|
|
5
|
+
if (campo.cardinalidade === "lista") {
|
|
6
|
+
anotacao = `list[${mapearTipoParaPython(campo.tipoItem ?? campo.tipoBase)}]`;
|
|
7
|
+
}
|
|
8
|
+
else if (campo.cardinalidade === "mapa") {
|
|
9
|
+
anotacao = `dict[${mapearTipoParaPython(campo.chaveMapa ?? "Texto")}, ${mapearTipoParaPython(campo.valorMapa ?? "Json")}]`;
|
|
10
|
+
}
|
|
11
|
+
else if (campo.cardinalidade === "uniao") {
|
|
12
|
+
anotacao = campo.tiposAlternativos.map((tipo) => mapearTipoParaPython(tipo)).join(" | ");
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
anotacao = mapearTipoParaPython(campo.tipoBase);
|
|
16
|
+
}
|
|
17
|
+
if (campo.opcional && !/\bNone\b/.test(anotacao)) {
|
|
18
|
+
return `${anotacao} | None`;
|
|
19
|
+
}
|
|
20
|
+
return anotacao;
|
|
21
|
+
}
|
|
22
|
+
function gerarDataclass(nome, campos) {
|
|
23
|
+
const camposOrdenados = [...campos].sort((a, b) => {
|
|
24
|
+
const obrigatorioA = a.modificadores.includes("required") ? 0 : 1;
|
|
25
|
+
const obrigatorioB = b.modificadores.includes("required") ? 0 : 1;
|
|
26
|
+
return obrigatorioA - obrigatorioB;
|
|
27
|
+
});
|
|
28
|
+
const linhas = camposOrdenados.length === 0
|
|
29
|
+
? " pass"
|
|
30
|
+
: camposOrdenados.map((campo) => {
|
|
31
|
+
const tipoBase = mapearCampoParaPython(campo);
|
|
32
|
+
if (campo.modificadores.includes("required")) {
|
|
33
|
+
return ` ${campo.nome}: ${tipoBase}`;
|
|
34
|
+
}
|
|
35
|
+
return ` ${campo.nome}: ${/\bNone\b/.test(tipoBase) ? tipoBase : `${tipoBase} | None`} = None`;
|
|
36
|
+
}).join("\n");
|
|
37
|
+
return `@dataclass\nclass ${nome}:\n${linhas}\n`;
|
|
38
|
+
}
|
|
39
|
+
function gerarComentarioInvariantesPython(invariantes) {
|
|
40
|
+
if (invariantes.length === 0) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
return `${invariantes.map((invariante) => `# Invariante: ${invariante.textoOriginal}`).join("\n")}\n`;
|
|
44
|
+
}
|
|
45
|
+
function gerarListaCamposPython(campos) {
|
|
46
|
+
if (campos.length === 0) {
|
|
47
|
+
return "[]";
|
|
48
|
+
}
|
|
49
|
+
return `[\n${campos.map((campo) => ` {"nome": "${campo.nome}", "tipo": "${campo.tipo}", "obrigatorio": ${campo.modificadores.includes("required") ? "True" : "False"}},`).join("\n")}\n]`;
|
|
50
|
+
}
|
|
51
|
+
function coletarTiposExternos(modulo) {
|
|
52
|
+
const locais = new Set([
|
|
53
|
+
...modulo.types.map((item) => item.nome),
|
|
54
|
+
...modulo.entities.map((item) => item.nome),
|
|
55
|
+
...modulo.enums.map((item) => item.nome),
|
|
56
|
+
]);
|
|
57
|
+
const referenciados = new Set();
|
|
58
|
+
const campos = [
|
|
59
|
+
...modulo.entities.flatMap((entity) => entity.campos),
|
|
60
|
+
...modulo.tasks.flatMap((task) => [...task.input, ...task.output]),
|
|
61
|
+
...modulo.routes.flatMap((route) => [...route.inputPublico, ...route.outputPublico]),
|
|
62
|
+
...modulo.states.flatMap((state) => state.campos),
|
|
63
|
+
];
|
|
64
|
+
for (const campo of campos) {
|
|
65
|
+
for (const tipo of extrairTiposNomeados(campo.tipo)) {
|
|
66
|
+
if (!locais.has(tipo)) {
|
|
67
|
+
referenciados.add(tipo);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return [...referenciados].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
72
|
+
}
|
|
73
|
+
function gerarMapaErrosPython(erros) {
|
|
74
|
+
const entradas = Object.entries(erros);
|
|
75
|
+
if (entradas.length === 0) {
|
|
76
|
+
return "{}";
|
|
77
|
+
}
|
|
78
|
+
return `{\n${entradas.map(([nome, mensagem]) => ` ${JSON.stringify(nome)}: ${JSON.stringify(mensagem)},`).join("\n")}\n}`;
|
|
79
|
+
}
|
|
80
|
+
function formatarValorPython(valor, camposConhecidos, variavel) {
|
|
81
|
+
const texto = valor.trim();
|
|
82
|
+
if (/^-?\d+(?:\.\d+)?$/.test(texto)) {
|
|
83
|
+
return texto;
|
|
84
|
+
}
|
|
85
|
+
if (texto === "verdadeiro") {
|
|
86
|
+
return "True";
|
|
87
|
+
}
|
|
88
|
+
if (texto === "falso") {
|
|
89
|
+
return "False";
|
|
90
|
+
}
|
|
91
|
+
if (texto === "nulo") {
|
|
92
|
+
return "None";
|
|
93
|
+
}
|
|
94
|
+
if (camposConhecidos.has(texto.split(".")[0] ?? texto)) {
|
|
95
|
+
return `${variavel}.${texto}`;
|
|
96
|
+
}
|
|
97
|
+
return JSON.stringify(texto);
|
|
98
|
+
}
|
|
99
|
+
function resolverExpressaoPython(expressao, camposConhecidos, variavel) {
|
|
100
|
+
switch (expressao.tipo) {
|
|
101
|
+
case "existe":
|
|
102
|
+
return `${variavel}.${expressao.alvo} is not None`;
|
|
103
|
+
case "comparacao":
|
|
104
|
+
return `${variavel}.${expressao.alvo} ${expressao.operador} ${formatarValorPython(expressao.valor, camposConhecidos, variavel)}`;
|
|
105
|
+
case "pertencimento":
|
|
106
|
+
return `${variavel}.${expressao.alvo} in [${(expressao.valores ?? []).map((valor) => formatarValorPython(valor, camposConhecidos, variavel)).join(", ")}]`;
|
|
107
|
+
case "predicado":
|
|
108
|
+
return "True";
|
|
109
|
+
case "composta":
|
|
110
|
+
return `(${expressao.termos.map((termo) => resolverExpressaoPython(termo, camposConhecidos, variavel)).join(expressao.operadorLogico === "e" ? " and " : " or ")})`;
|
|
111
|
+
case "negacao":
|
|
112
|
+
return `(not ${resolverExpressaoPython(expressao.termo, camposConhecidos, variavel)})`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function valorPadraoPython(campo) {
|
|
116
|
+
const tipo = campo.tipoBase;
|
|
117
|
+
const nomeCampo = campo.nome;
|
|
118
|
+
if (campo.cardinalidade === "lista") {
|
|
119
|
+
return "[]";
|
|
120
|
+
}
|
|
121
|
+
if (campo.cardinalidade === "mapa") {
|
|
122
|
+
return "{}";
|
|
123
|
+
}
|
|
124
|
+
if (campo.opcional) {
|
|
125
|
+
return "None";
|
|
126
|
+
}
|
|
127
|
+
switch (tipo) {
|
|
128
|
+
case "Texto":
|
|
129
|
+
case "Id":
|
|
130
|
+
case "Email":
|
|
131
|
+
case "Url":
|
|
132
|
+
return JSON.stringify(`${nomeCampo}_exemplo`);
|
|
133
|
+
case "Numero":
|
|
134
|
+
case "Inteiro":
|
|
135
|
+
case "Decimal":
|
|
136
|
+
return "1";
|
|
137
|
+
case "Booleano":
|
|
138
|
+
return "False";
|
|
139
|
+
case "Json":
|
|
140
|
+
return "{}";
|
|
141
|
+
default:
|
|
142
|
+
return "SimpleNamespace()";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function removerAspasExternas(valor) {
|
|
146
|
+
const texto = valor.trim();
|
|
147
|
+
if ((texto.startsWith("\"") && texto.endsWith("\""))
|
|
148
|
+
|| (texto.startsWith("'") && texto.endsWith("'"))) {
|
|
149
|
+
return texto.slice(1, -1);
|
|
150
|
+
}
|
|
151
|
+
return texto;
|
|
152
|
+
}
|
|
153
|
+
function dividirLiteralNoNivelRaiz(valor, separador) {
|
|
154
|
+
const partes = [];
|
|
155
|
+
let atual = "";
|
|
156
|
+
let profundidadeAngular = 0;
|
|
157
|
+
let profundidadeLista = 0;
|
|
158
|
+
let profundidadeMapa = 0;
|
|
159
|
+
let aspas = null;
|
|
160
|
+
for (let indice = 0; indice < valor.length; indice += 1) {
|
|
161
|
+
const caractere = valor[indice];
|
|
162
|
+
const anterior = indice > 0 ? valor[indice - 1] : "";
|
|
163
|
+
if (aspas) {
|
|
164
|
+
atual += caractere;
|
|
165
|
+
if (caractere === aspas && anterior !== "\\") {
|
|
166
|
+
aspas = null;
|
|
167
|
+
}
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (caractere === "\"" || caractere === "'") {
|
|
171
|
+
aspas = caractere;
|
|
172
|
+
atual += caractere;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (caractere === "<") {
|
|
176
|
+
profundidadeAngular += 1;
|
|
177
|
+
atual += caractere;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (caractere === ">") {
|
|
181
|
+
profundidadeAngular = Math.max(0, profundidadeAngular - 1);
|
|
182
|
+
atual += caractere;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (caractere === "[") {
|
|
186
|
+
profundidadeLista += 1;
|
|
187
|
+
atual += caractere;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (caractere === "]") {
|
|
191
|
+
profundidadeLista = Math.max(0, profundidadeLista - 1);
|
|
192
|
+
atual += caractere;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (caractere === "{") {
|
|
196
|
+
profundidadeMapa += 1;
|
|
197
|
+
atual += caractere;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (caractere === "}") {
|
|
201
|
+
profundidadeMapa = Math.max(0, profundidadeMapa - 1);
|
|
202
|
+
atual += caractere;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (caractere === separador
|
|
206
|
+
&& profundidadeAngular === 0
|
|
207
|
+
&& profundidadeLista === 0
|
|
208
|
+
&& profundidadeMapa === 0) {
|
|
209
|
+
if (atual.trim()) {
|
|
210
|
+
partes.push(atual.trim());
|
|
211
|
+
}
|
|
212
|
+
atual = "";
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
atual += caractere;
|
|
216
|
+
}
|
|
217
|
+
if (atual.trim()) {
|
|
218
|
+
partes.push(atual.trim());
|
|
219
|
+
}
|
|
220
|
+
return partes;
|
|
221
|
+
}
|
|
222
|
+
function resolverTipoItemTeste(tipoDeclarado) {
|
|
223
|
+
const tipo = (tipoDeclarado ?? "").trim();
|
|
224
|
+
if (!tipo) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
if (tipo.endsWith("[]")) {
|
|
228
|
+
return tipo.slice(0, -2).trim();
|
|
229
|
+
}
|
|
230
|
+
const lista = tipo.match(/^Lista<(.+)>$/);
|
|
231
|
+
if (lista?.[1]) {
|
|
232
|
+
return lista[1].trim();
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
function formatarLiteralTestePython(valor, tipoDeclarado) {
|
|
237
|
+
const bruto = valor.trim();
|
|
238
|
+
if (bruto.startsWith("[") && bruto.endsWith("]")) {
|
|
239
|
+
const interior = bruto.slice(1, -1).trim();
|
|
240
|
+
const tipoItem = resolverTipoItemTeste(tipoDeclarado);
|
|
241
|
+
if (!interior) {
|
|
242
|
+
return "[]";
|
|
243
|
+
}
|
|
244
|
+
return `[${dividirLiteralNoNivelRaiz(interior, ",").map((item) => formatarLiteralTestePython(item, tipoItem)).join(", ")}]`;
|
|
245
|
+
}
|
|
246
|
+
if (bruto.startsWith("{") && bruto.endsWith("}")) {
|
|
247
|
+
const interior = bruto.slice(1, -1).trim();
|
|
248
|
+
if (!interior) {
|
|
249
|
+
return "{}";
|
|
250
|
+
}
|
|
251
|
+
const pares = dividirLiteralNoNivelRaiz(interior, ",")
|
|
252
|
+
.map((par) => {
|
|
253
|
+
const [chaveBruta, ...valorBruto] = dividirLiteralNoNivelRaiz(par, ":");
|
|
254
|
+
if (!chaveBruta || valorBruto.length === 0) {
|
|
255
|
+
return "";
|
|
256
|
+
}
|
|
257
|
+
return `${JSON.stringify(removerAspasExternas(chaveBruta))}: ${formatarLiteralTestePython(valorBruto.join(":"))}`;
|
|
258
|
+
})
|
|
259
|
+
.filter(Boolean);
|
|
260
|
+
return `{${pares.join(", ")}}`;
|
|
261
|
+
}
|
|
262
|
+
const texto = removerAspasExternas(bruto);
|
|
263
|
+
if (["Texto", "Id", "Email", "Url"].includes(tipoDeclarado ?? "")) {
|
|
264
|
+
return JSON.stringify(texto);
|
|
265
|
+
}
|
|
266
|
+
if (["Numero", "Inteiro", "Decimal"].includes(tipoDeclarado ?? "") && /^-?\d+(?:\.\d+)?$/.test(texto)) {
|
|
267
|
+
return texto;
|
|
268
|
+
}
|
|
269
|
+
if ((tipoDeclarado ?? "") === "Booleano") {
|
|
270
|
+
if (texto === "verdadeiro") {
|
|
271
|
+
return "True";
|
|
272
|
+
}
|
|
273
|
+
if (texto === "falso") {
|
|
274
|
+
return "False";
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (/^-?\d+(?:\.\d+)?$/.test(texto)) {
|
|
278
|
+
return texto;
|
|
279
|
+
}
|
|
280
|
+
if (texto === "verdadeiro") {
|
|
281
|
+
return "True";
|
|
282
|
+
}
|
|
283
|
+
if (texto === "falso") {
|
|
284
|
+
return "False";
|
|
285
|
+
}
|
|
286
|
+
if (texto === "nulo") {
|
|
287
|
+
return "None";
|
|
288
|
+
}
|
|
289
|
+
return JSON.stringify(texto);
|
|
290
|
+
}
|
|
291
|
+
function gerarMapaLiteralPython(campos) {
|
|
292
|
+
return `{${campos.map((campo) => `${JSON.stringify(campo.nome)}: ${campo.valor}`).join(", ")}}`;
|
|
293
|
+
}
|
|
294
|
+
function coletarTiposCompostos(modulo) {
|
|
295
|
+
const tipos = new Map();
|
|
296
|
+
for (const type of modulo.types) {
|
|
297
|
+
tipos.set(type.nome, new Map(type.definicao.campos.map((campo) => [campo.nome, campo.tipo])));
|
|
298
|
+
}
|
|
299
|
+
for (const entity of modulo.entities) {
|
|
300
|
+
tipos.set(entity.nome, new Map(entity.campos.map((campo) => [campo.nome, campo.tipo])));
|
|
301
|
+
}
|
|
302
|
+
return tipos;
|
|
303
|
+
}
|
|
304
|
+
function gerarLiteralBlocoTestePython(bloco, tiposCompostos, tiposDeclarados, tipoAtual) {
|
|
305
|
+
const entradas = [];
|
|
306
|
+
const tiposAtuais = tipoAtual ? tiposCompostos?.get(tipoAtual) : undefined;
|
|
307
|
+
for (const campo of bloco.campos) {
|
|
308
|
+
entradas.push({
|
|
309
|
+
nome: campo.nome,
|
|
310
|
+
valor: formatarLiteralTestePython(campo.tipo, tiposDeclarados?.get(campo.nome) ?? tiposAtuais?.get(campo.nome)),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
for (const subbloco of bloco.blocos) {
|
|
314
|
+
const tipoSubbloco = tiposDeclarados?.get(subbloco.nome) ?? tiposAtuais?.get(subbloco.nome);
|
|
315
|
+
entradas.push({
|
|
316
|
+
nome: subbloco.nome,
|
|
317
|
+
valor: gerarLiteralBlocoTestePython(subbloco.conteudo, tiposCompostos, undefined, tipoSubbloco),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (tipoAtual && tiposCompostos?.has(tipoAtual)) {
|
|
321
|
+
return `${tipoAtual}(${entradas.map((campo) => `${campo.nome}=${campo.valor}`).join(", ")})`;
|
|
322
|
+
}
|
|
323
|
+
return gerarMapaLiteralPython(entradas);
|
|
324
|
+
}
|
|
325
|
+
function paraPascalCase(valor) {
|
|
326
|
+
return valor
|
|
327
|
+
.split(/[^A-Za-z0-9]+/)
|
|
328
|
+
.filter(Boolean)
|
|
329
|
+
.map((parte) => parte[0].toUpperCase() + parte.slice(1))
|
|
330
|
+
.join("");
|
|
331
|
+
}
|
|
332
|
+
function gerarPreparacaoSaida(task) {
|
|
333
|
+
const camposSaida = new Set(task.output.map((campo) => campo.nome));
|
|
334
|
+
const argumentos = task.output.map((campo) => `${campo.nome}=${valorPadraoPython(campo)}`).join(", ");
|
|
335
|
+
const ajustes = [];
|
|
336
|
+
for (const garantia of task.garantiasEstruturadas) {
|
|
337
|
+
if (garantia.tipo === "pertencimento" && garantia.valores && camposSaida.has(garantia.alvo)) {
|
|
338
|
+
ajustes.push(` saida.${garantia.alvo} = ${formatarValorPython(garantia.valores[0] ?? "", camposSaida, "saida")}`);
|
|
339
|
+
}
|
|
340
|
+
if (garantia.tipo === "comparacao" && garantia.valor && camposSaida.has(garantia.alvo.split(".")[0] ?? garantia.alvo) && !garantia.alvo.includes(".")) {
|
|
341
|
+
ajustes.push(` saida.${garantia.alvo} = ${formatarValorPython(garantia.valor, camposSaida, "saida")}`);
|
|
342
|
+
}
|
|
343
|
+
if (garantia.tipo === "comparacao" && garantia.valor && garantia.alvo.includes(".")) {
|
|
344
|
+
const [raiz, filho] = garantia.alvo.split(".", 2);
|
|
345
|
+
if (raiz && filho && camposSaida.has(raiz)) {
|
|
346
|
+
ajustes.push(` if saida.${raiz} is None:\n saida.${raiz} = SimpleNamespace()`);
|
|
347
|
+
ajustes.push(` saida.${raiz}.${filho} = ${formatarValorPython(garantia.valor, camposSaida, "saida")}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (garantia.tipo === "existe" && garantia.alvo.includes(".")) {
|
|
351
|
+
const [raiz, filho] = garantia.alvo.split(".", 2);
|
|
352
|
+
if (raiz && filho && camposSaida.has(raiz)) {
|
|
353
|
+
ajustes.push(` if saida.${raiz} is None:\n saida.${raiz} = SimpleNamespace()`);
|
|
354
|
+
ajustes.push(` if getattr(saida.${raiz}, "${filho}", None) is None:\n saida.${raiz}.${filho} = "valor_garantido"`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return ` saida = ${task.nome}Saida(${argumentos})\n${ajustes.join("\n")}`;
|
|
359
|
+
}
|
|
360
|
+
function finalizarBlocoPython(linhas) {
|
|
361
|
+
if (linhas.length === 0) {
|
|
362
|
+
return " pass";
|
|
363
|
+
}
|
|
364
|
+
const possuiInstrucaoExecutavel = linhas.some((linha) => {
|
|
365
|
+
const texto = linha.trim();
|
|
366
|
+
return texto.length > 0 && !texto.startsWith("#");
|
|
367
|
+
});
|
|
368
|
+
if (possuiInstrucaoExecutavel) {
|
|
369
|
+
return linhas.join("\n");
|
|
370
|
+
}
|
|
371
|
+
return `${linhas.join("\n")}\n pass`;
|
|
372
|
+
}
|
|
373
|
+
function gerarFuncaoGarantias(task) {
|
|
374
|
+
const camposSaida = new Set(task.output.map((campo) => campo.nome));
|
|
375
|
+
const linhas = [
|
|
376
|
+
...task.garantiasEstruturadas.map((garantia) => {
|
|
377
|
+
switch (garantia.tipo) {
|
|
378
|
+
case "predicado":
|
|
379
|
+
return ` # Predicado de garantia declarado em Sema: ${garantia.textoOriginal}`;
|
|
380
|
+
default:
|
|
381
|
+
return ` if not (${resolverExpressaoPython(garantia, camposSaida, "saida")}):\n raise ValueError("Garantia violada: ${garantia.textoOriginal}")`;
|
|
382
|
+
}
|
|
383
|
+
}),
|
|
384
|
+
...task.guarantees
|
|
385
|
+
.filter((garantia) => !task.garantiasEstruturadas.some((estruturada) => estruturada.textoOriginal === garantia))
|
|
386
|
+
.map((garantia) => ` # Garantia declarada em Sema: ${garantia}`),
|
|
387
|
+
];
|
|
388
|
+
return `def verificar_garantias_${normalizarNomeParaSimbolo(task.nome)}(saida: ${task.nome}Saida) -> None:\n${finalizarBlocoPython(linhas)}\n`;
|
|
389
|
+
}
|
|
390
|
+
function gerarMetadadosTask(task) {
|
|
391
|
+
const efeitos = task.efeitosEstruturados.length === 0
|
|
392
|
+
? "[]"
|
|
393
|
+
: `[\n${task.efeitosEstruturados.map((efeito) => ` {"categoria": "${efeito.categoria}", "alvo": "${efeito.alvo}"${efeito.detalhe ? `, "detalhe": ${JSON.stringify(efeito.detalhe)}` : ""}${efeito.criticidade ? `, "criticidade": "${efeito.criticidade}"` : ""}},`).join("\n")}\n]`;
|
|
394
|
+
const implementacoes = task.implementacoesExternas.length === 0
|
|
395
|
+
? "[]"
|
|
396
|
+
: `[\n${task.implementacoesExternas.map((impl) => ` {"origem": "${impl.origem}", "caminho": "${impl.caminho}", "resolucaoImpl": "${impl.resolucaoImpl ?? impl.caminho}", "statusImpl": "${impl.statusImpl ?? "nao_verificado"}"},`).join("\n")}\n]`;
|
|
397
|
+
return `contrato_${normalizarNomeParaSimbolo(task.nome)} = {
|
|
398
|
+
"nome": "${task.nome}",
|
|
399
|
+
"input": ${gerarListaCamposPython(task.input)},
|
|
400
|
+
"output": ${gerarListaCamposPython(task.output)},
|
|
401
|
+
"effects": ${efeitos},
|
|
402
|
+
"impl": ${implementacoes},
|
|
403
|
+
"errors": ${gerarMapaErrosPython(task.errors)},
|
|
404
|
+
"guarantees": ${JSON.stringify(task.guarantees, null, 2)},
|
|
405
|
+
}
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
408
|
+
function gerarMapeamentoSaidaPublicaPython(nomeVariavel, campos) {
|
|
409
|
+
if (campos.length === 0) {
|
|
410
|
+
return "{}";
|
|
411
|
+
}
|
|
412
|
+
return `{\n${campos.map((campo) => ` "${campo.nome}": ${nomeVariavel}.${campo.nome},`).join("\n")}\n }`;
|
|
413
|
+
}
|
|
414
|
+
function gerarValidacoesRespostaPublicaPython(campos) {
|
|
415
|
+
const obrigatorios = campos.filter((campo) => campo.modificadores.includes("required"));
|
|
416
|
+
if (obrigatorios.length === 0) {
|
|
417
|
+
return " pass";
|
|
418
|
+
}
|
|
419
|
+
return obrigatorios
|
|
420
|
+
.map((campo) => ` if dados.${campo.nome} is None:\n raise ValueError("Resposta publica invalida: campo obrigatorio ausente ${campo.nome}")`)
|
|
421
|
+
.join("\n");
|
|
422
|
+
}
|
|
423
|
+
function gerarRotas(modulo) {
|
|
424
|
+
const rotasComTask = modulo.routes.filter((route) => route.task);
|
|
425
|
+
if (rotasComTask.length === 0) {
|
|
426
|
+
return "";
|
|
427
|
+
}
|
|
428
|
+
return rotasComTask.map((route) => {
|
|
429
|
+
const taskAssociada = modulo.tasks.find((task) => task.nome === route.task);
|
|
430
|
+
if (!taskAssociada) {
|
|
431
|
+
return "";
|
|
432
|
+
}
|
|
433
|
+
const nomeSimboloRoute = normalizarNomeParaSimbolo(route.nome);
|
|
434
|
+
const nomeSimboloTask = normalizarNomeParaSimbolo(taskAssociada.nome);
|
|
435
|
+
const efeitosPublicos = route.efeitosPublicos.length === 0
|
|
436
|
+
? "[]"
|
|
437
|
+
: `[\n${route.efeitosPublicos.map((efeito) => ` {"categoria": "${efeito.categoria}", "alvo": "${efeito.alvo}"${efeito.detalhe ? `, "detalhe": ${JSON.stringify(efeito.detalhe)}` : ""}${efeito.criticidade ? `, "criticidade": "${efeito.criticidade}"` : ""}},`).join("\n")}\n]`;
|
|
438
|
+
const mapaErros = route.errosPublicos.length === 0
|
|
439
|
+
? "{}"
|
|
440
|
+
: `{\n${route.errosPublicos.map((erro) => ` ${JSON.stringify(erro.nome)}: ${JSON.stringify(erro.mensagem ?? taskAssociada.errors[erro.nome] ?? "")},`).join("\n")}\n}`;
|
|
441
|
+
const blocosErro = route.errosPublicos.map((erro) => ` except ${taskAssociada.nome}_${erro.nome}Erro:
|
|
442
|
+
return {"sucesso": False, "erro": {"codigo": "${erro.nome}", "mensagem": ${JSON.stringify(erro.mensagem ?? taskAssociada.errors[erro.nome] ?? `Erro publico ${erro.nome}`)}}}`).join("\n");
|
|
443
|
+
return `
|
|
444
|
+
${gerarDataclass(`${route.nome}EntradaPublica`, route.inputPublico)}
|
|
445
|
+
${gerarDataclass(`${route.nome}SaidaPublica`, route.outputPublico)}
|
|
446
|
+
contrato_publico_${nomeSimboloRoute} = {
|
|
447
|
+
"nome": "${route.nome}",
|
|
448
|
+
"metodo": ${JSON.stringify(route.metodo ?? null)},
|
|
449
|
+
"caminho": ${JSON.stringify(route.caminho ?? null)},
|
|
450
|
+
"task": ${JSON.stringify(route.task ?? null)},
|
|
451
|
+
"input": ${gerarListaCamposPython(route.inputPublico)},
|
|
452
|
+
"output": ${gerarListaCamposPython(route.outputPublico)},
|
|
453
|
+
"effects": ${efeitosPublicos},
|
|
454
|
+
"guarantees": ${JSON.stringify(route.garantiasPublicasMinimas, null, 4)},
|
|
455
|
+
"errors": ${mapaErros},
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
def verificar_resposta_publica_${nomeSimboloRoute}(dados: ${route.nome}SaidaPublica) -> None:
|
|
459
|
+
${gerarValidacoesRespostaPublicaPython(route.outputPublico)}
|
|
460
|
+
|
|
461
|
+
def adaptar_${nomeSimboloRoute}(requisicao: ${route.nome}EntradaPublica) -> dict[str, object]:
|
|
462
|
+
try:
|
|
463
|
+
saida = executar_${nomeSimboloTask}(requisicao) # type: ignore[arg-type]
|
|
464
|
+
dados_publicos = ${route.nome}SaidaPublica(${route.outputPublico.map((campo) => `${campo.nome}=saida.${campo.nome}`).join(", ")})
|
|
465
|
+
verificar_resposta_publica_${nomeSimboloRoute}(dados_publicos)
|
|
466
|
+
return {
|
|
467
|
+
"sucesso": True,
|
|
468
|
+
"dados": ${gerarMapeamentoSaidaPublicaPython("dados_publicos", route.outputPublico)},
|
|
469
|
+
}
|
|
470
|
+
${blocosErro || " except Exception:\n raise"}
|
|
471
|
+
except Exception:
|
|
472
|
+
raise
|
|
473
|
+
`;
|
|
474
|
+
}).join("\n");
|
|
475
|
+
}
|
|
476
|
+
function gerarTask(task, tiposCompostos) {
|
|
477
|
+
const nome = normalizarNomeParaSimbolo(task.nome);
|
|
478
|
+
const camposEntrada = new Set(task.input.map((campo) => campo.nome));
|
|
479
|
+
const camposSaida = new Set(task.output.map((campo) => campo.nome));
|
|
480
|
+
const errosMapeados = new Map(Object.entries(task.errors));
|
|
481
|
+
for (const caso of task.tests) {
|
|
482
|
+
const tipoErro = caso.error?.campos.find((campo) => campo.nome === "tipo")?.tipo ?? caso.error?.campos[0]?.tipo;
|
|
483
|
+
if (tipoErro && !errosMapeados.has(tipoErro)) {
|
|
484
|
+
errosMapeados.set(tipoErro, `Erro sintetico gerado a partir do caso de teste "${caso.nome}".`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const erros = [...errosMapeados.entries()];
|
|
488
|
+
const tiposEntrada = new Map(task.input.map((campo) => [campo.nome, campo.tipo]));
|
|
489
|
+
const erroAutenticacao = erros.find(([nomeErro]) => nomeErro.includes("autentic"))?.[0];
|
|
490
|
+
const erroAutorizacao = erros.find(([nomeErro]) => nomeErro.includes("acesso_negado") || nomeErro.includes("autoriz"))?.[0];
|
|
491
|
+
const validacoes = [
|
|
492
|
+
...task.input
|
|
493
|
+
.filter((campo) => campo.modificadores.includes("required"))
|
|
494
|
+
.map((campo) => ` if entrada.${campo.nome} is None:\n raise ValueError("Campo obrigatorio ausente: ${campo.nome}")`),
|
|
495
|
+
...task.regrasEstruturadas.map((regra) => {
|
|
496
|
+
switch (regra.tipo) {
|
|
497
|
+
case "predicado":
|
|
498
|
+
return ` # Predicado declarado em Sema: ${regra.textoOriginal}`;
|
|
499
|
+
default:
|
|
500
|
+
return ` if not (${resolverExpressaoPython(regra, camposEntrada, "entrada")}):\n raise ValueError("Regra violada: ${regra.textoOriginal}")`;
|
|
501
|
+
}
|
|
502
|
+
}),
|
|
503
|
+
...task.rules
|
|
504
|
+
.filter((regra) => !task.regrasEstruturadas.some((estruturada) => estruturada.textoOriginal === regra))
|
|
505
|
+
.map((regra) => ` # Regra declarada em Sema: ${regra}`),
|
|
506
|
+
];
|
|
507
|
+
const efeitos = task.efeitosEstruturados.length > 0
|
|
508
|
+
? task.efeitosEstruturados.map((efeito) => ` # Efeito estruturado: categoria=${efeito.categoria} alvo=${efeito.alvo}${efeito.detalhe ? ` detalhe=${efeito.detalhe}` : ""}${efeito.criticidade ? ` criticidade=${efeito.criticidade}` : ""}`).join("\n")
|
|
509
|
+
: task.effects.length === 0
|
|
510
|
+
? " # Nenhum efeito declarado."
|
|
511
|
+
: task.effects.map((efeito) => ` # Efeito declarado: ${efeito}`).join("\n");
|
|
512
|
+
const implementacoes = task.implementacoesExternas.length > 0
|
|
513
|
+
? task.implementacoesExternas.map((impl) => ` # Implementacao externa vinculada: origem=${impl.origem} caminho=${impl.caminho} status=${impl.statusImpl ?? "nao_verificado"}`).join("\n")
|
|
514
|
+
: "";
|
|
515
|
+
const garantias = ` verificar_garantias_${nome}(saida)\n return saida`;
|
|
516
|
+
return `
|
|
517
|
+
${gerarDataclass(`${task.nome}Entrada`, task.input)}
|
|
518
|
+
${gerarDataclass(`${task.nome}Saida`, task.output)}
|
|
519
|
+
${erros.map(([nomeErro, mensagem]) => `\nclass ${task.nome}_${nomeErro}Erro(Exception):\n codigo = "${nomeErro}"\n\n def __init__(self) -> None:\n super().__init__(${JSON.stringify(mensagem)})\n`).join("\n")}
|
|
520
|
+
|
|
521
|
+
def normalizar_contexto_${nome}(contexto: dict[str, object] | None = None) -> dict[str, object]:
|
|
522
|
+
contexto_normalizado: dict[str, object] = dict(contexto or {})
|
|
523
|
+
contexto_normalizado.setdefault("autenticado", True)
|
|
524
|
+
contexto_normalizado.setdefault("autorizado", True)
|
|
525
|
+
contexto_normalizado.setdefault("erro_esperado", None)
|
|
526
|
+
return contexto_normalizado
|
|
527
|
+
|
|
528
|
+
def criar_erro_${nome}(codigo: str) -> Exception:
|
|
529
|
+
${finalizarBlocoPython(erros.map(([nomeErro]) => ` if codigo == "${nomeErro}":\n return ${task.nome}_${nomeErro}Erro()`).concat(` return Exception(f"Erro sintetico nao mapeado para ${task.nome}: {codigo}")`))}
|
|
530
|
+
|
|
531
|
+
${gerarMetadadosTask(task)}
|
|
532
|
+
|
|
533
|
+
def validar_${nome}(entrada: ${task.nome}Entrada) -> None:
|
|
534
|
+
${finalizarBlocoPython(validacoes)}
|
|
535
|
+
|
|
536
|
+
${gerarFuncaoGarantias(task)}
|
|
537
|
+
|
|
538
|
+
def executar_${nome}(entrada: ${task.nome}Entrada, contexto: dict[str, object] | None = None) -> ${task.nome}Saida:
|
|
539
|
+
contexto_execucao = normalizar_contexto_${nome}(contexto)
|
|
540
|
+
${erroAutenticacao ? ` if contexto_execucao["erro_esperado"] == "${erroAutenticacao}" or (${JSON.stringify(task.auth.modo ?? "")} == "obrigatorio" and not bool(contexto_execucao["autenticado"])):\n raise ${task.nome}_${erroAutenticacao}Erro()` : ""}
|
|
541
|
+
${erroAutorizacao ? ` if contexto_execucao["erro_esperado"] == "${erroAutorizacao}" or (${task.authz.explicita ? "not bool(contexto_execucao[\"autorizado\"])" : "False"}):\n raise ${task.nome}_${erroAutorizacao}Erro()` : ""}
|
|
542
|
+
if contexto_execucao["erro_esperado"] is not None${erroAutenticacao ? ` and contexto_execucao["erro_esperado"] != "${erroAutenticacao}"` : ""}${erroAutorizacao ? ` and contexto_execucao["erro_esperado"] != "${erroAutorizacao}"` : ""}:
|
|
543
|
+
raise criar_erro_${nome}(str(contexto_execucao["erro_esperado"]))
|
|
544
|
+
validar_${nome}(entrada)
|
|
545
|
+
${task.stateContract ? ` # Vinculo de estado: ${task.stateContract.nomeEstado ?? "nao_definido"}\n # Transicoes declaradas pela task: ${task.stateContract.transicoes.map((transicao) => `${transicao.origem}->${transicao.destino}`).join(", ") || "nenhuma"}` : ""}
|
|
546
|
+
${implementacoes}
|
|
547
|
+
${efeitos}
|
|
548
|
+
${gerarPreparacaoSaida(task)}
|
|
549
|
+
${garantias}
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
function gerarTestes(modulo) {
|
|
553
|
+
const linhas = ["import pytest", `from ${normalizarNomeModulo(modulo.nome).replace(/\./g, "_")} import *`, ""];
|
|
554
|
+
const tiposCompostos = coletarTiposCompostos(modulo);
|
|
555
|
+
for (const task of modulo.tasks) {
|
|
556
|
+
const nomeFuncao = `executar_${normalizarNomeParaSimbolo(task.nome)}`;
|
|
557
|
+
const tiposEntrada = new Map(task.input.map((campo) => [campo.nome, campo.tipo]));
|
|
558
|
+
for (const caso of task.tests) {
|
|
559
|
+
const argumentos = [
|
|
560
|
+
...caso.given.campos.map((campo) => `${campo.nome}=${formatarLiteralTestePython(campo.tipo, tiposEntrada.get(campo.nome))}`),
|
|
561
|
+
...caso.given.blocos.map((subbloco) => `${subbloco.nome}=${gerarLiteralBlocoTestePython(subbloco.conteudo, tiposCompostos, undefined, tiposEntrada.get(subbloco.nome))}`),
|
|
562
|
+
].join(", ");
|
|
563
|
+
const tipoErro = caso.error?.campos.find((campo) => campo.nome === "tipo")?.tipo ?? caso.error?.campos[0]?.tipo;
|
|
564
|
+
if (tipoErro) {
|
|
565
|
+
const contextoLinhas = [`"erro_esperado": ${JSON.stringify(tipoErro)}`];
|
|
566
|
+
if (tipoErro.includes("autentic")) {
|
|
567
|
+
contextoLinhas.push('"autenticado": False', '"autorizado": False');
|
|
568
|
+
}
|
|
569
|
+
else if (tipoErro.includes("acesso_negado") || tipoErro.includes("autoriz")) {
|
|
570
|
+
contextoLinhas.push('"autenticado": True', '"autorizado": False');
|
|
571
|
+
}
|
|
572
|
+
linhas.push(`def test_${normalizarNomeParaSimbolo(task.nome)}_${normalizarNomeParaSimbolo(caso.nome)}() -> None:\n entrada = ${task.nome}Entrada(${argumentos})\n contexto = { ${contextoLinhas.join(", ")} }\n with pytest.raises(${task.nome}_${tipoErro}Erro):\n ${nomeFuncao}(entrada, contexto)\n`);
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
linhas.push(`def test_${normalizarNomeParaSimbolo(task.nome)}_${normalizarNomeParaSimbolo(caso.nome)}() -> None:\n entrada = ${task.nome}Entrada(${argumentos})\n resultado = ${nomeFuncao}(entrada)\n assert resultado is not None\n`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return linhas.join("\n");
|
|
579
|
+
}
|
|
580
|
+
function gerarPythonBase(modulo) {
|
|
581
|
+
const nomeBase = normalizarNomeModulo(modulo.nome).replace(/\./g, "_");
|
|
582
|
+
const interoperabilidades = modulo.interoperabilidades
|
|
583
|
+
.map((interop) => `# Interop externo ${interop.origem}: ${interop.caminho}`)
|
|
584
|
+
.join("\n");
|
|
585
|
+
const tiposExternos = coletarTiposExternos(modulo)
|
|
586
|
+
.map((tipo) => `class ${tipo}(SimpleNamespace):\n pass\n`)
|
|
587
|
+
.join("\n");
|
|
588
|
+
const enums = modulo.enums.map((enumeracao) => `class ${enumeracao.nome}:\n${enumeracao.valores.map((valor) => ` ${valor} = "${valor}"`).join("\n")}\n`).join("\n");
|
|
589
|
+
const tipos = modulo.types.map((type) => `${gerarComentarioInvariantesPython(type.invariantes)}${gerarDataclass(type.nome, type.definicao.campos)}`).join("\n");
|
|
590
|
+
const entidades = modulo.entities.map((entity) => `${gerarComentarioInvariantesPython(entity.invariantes)}${gerarDataclass(entity.nome, entity.campos)}`).join("\n");
|
|
591
|
+
const states = modulo.states.map((state) => `# State${state.nome ? ` ${state.nome}` : ""}: campos=${state.campos.length} invariantes=${state.invariantes.length} transicoes=${state.transicoes.length}`).join("\n");
|
|
592
|
+
const flows = modulo.flows.map((flow) => `# Flow ${flow.nome}: etapas=${flow.linhas.length} tasks=${flow.tasksReferenciadas.join(", ") || "nenhuma"} ramificacoes=${flow.etapasEstruturadas.filter((etapa) => etapa.emSucesso || etapa.emErro).length} mapeamentos=${flow.etapasEstruturadas.reduce((total, etapa) => total + etapa.mapeamentos.length, 0)} rotas_erro=${flow.etapasEstruturadas.reduce((total, etapa) => total + etapa.porErro.length, 0)} efeitos=${flow.efeitosEstruturados.map((efeito) => `${efeito.categoria}:${efeito.alvo}`).join(", ") || "nenhum"}`).join("\n");
|
|
593
|
+
const routes = modulo.routes.map((route) => `# Route ${route.nome}: metodo=${route.metodo ?? "nao_definido"} caminho=${route.caminho ?? "nao_definido"} task=${route.task ?? "nao_definida"} input_publico=${route.inputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} output_publico=${route.outputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} erros_publicos=${route.errosPublicos.map((erro) => erro.nome).join(", ") || "padrao_task"} effects_publicos=${route.efeitosPublicos.map((efeito) => `${efeito.categoria}:${efeito.alvo}`).join(", ") || "nenhum"} garantias_publicas=${route.garantiasPublicasMinimas.length}`).join("\n");
|
|
594
|
+
const tiposCompostos = coletarTiposCompostos(modulo);
|
|
595
|
+
const tasks = modulo.tasks.map((task) => gerarTask(task, tiposCompostos)).join("\n");
|
|
596
|
+
const contratosPublicos = gerarRotas(modulo);
|
|
597
|
+
const codigo = `# Arquivo gerado automaticamente pela Sema.\n# Modulo de origem: ${modulo.nome}\nfrom __future__ import annotations\n${interoperabilidades ? `${interoperabilidades}\n` : ""}\nfrom dataclasses import dataclass\nfrom types import SimpleNamespace\n\n${tiposExternos}\n${tipos}\n${enums}\n${entidades}\n${states}\n${flows}\n${routes}\n${tasks}\n${contratosPublicos}\n`;
|
|
598
|
+
const testes = gerarTestes(modulo);
|
|
599
|
+
return [
|
|
600
|
+
{ caminhoRelativo: `${nomeBase}.py`, conteudo: codigo },
|
|
601
|
+
{ caminhoRelativo: `test_${nomeBase}.py`, conteudo: testes },
|
|
602
|
+
];
|
|
603
|
+
}
|
|
604
|
+
function gerarFastApiSchemas(modulo, caminhoContrato) {
|
|
605
|
+
const linhas = [
|
|
606
|
+
"from pydantic import BaseModel",
|
|
607
|
+
`from ${caminhoContrato} import *`,
|
|
608
|
+
"",
|
|
609
|
+
];
|
|
610
|
+
for (const task of modulo.tasks) {
|
|
611
|
+
linhas.push(`class ${task.nome}EntradaSchema(BaseModel):
|
|
612
|
+
${task.input.length === 0 ? " pass" : task.input.map((campo) => ` ${campo.nome}: ${mapearCampoParaPython(campo)}`).join("\n")}
|
|
613
|
+
`);
|
|
614
|
+
linhas.push(`class ${task.nome}SaidaSchema(BaseModel):
|
|
615
|
+
${task.output.length === 0 ? " pass" : task.output.map((campo) => ` ${campo.nome}: ${mapearCampoParaPython(campo)}`).join("\n")}
|
|
616
|
+
`);
|
|
617
|
+
}
|
|
618
|
+
for (const route of modulo.routes) {
|
|
619
|
+
linhas.push(`class ${route.nome}EntradaPublicaSchema(BaseModel):
|
|
620
|
+
${route.inputPublico.length === 0 ? " pass" : route.inputPublico.map((campo) => ` ${campo.nome}: ${mapearCampoParaPython(campo)}`).join("\n")}
|
|
621
|
+
`);
|
|
622
|
+
linhas.push(`class ${route.nome}SaidaPublicaSchema(BaseModel):
|
|
623
|
+
${route.outputPublico.length === 0 ? " pass" : route.outputPublico.map((campo) => ` ${campo.nome}: ${mapearCampoParaPython(campo)}`).join("\n")}
|
|
624
|
+
`);
|
|
625
|
+
}
|
|
626
|
+
return linhas.join("\n");
|
|
627
|
+
}
|
|
628
|
+
function gerarFastApiService(modulo, caminhoContrato) {
|
|
629
|
+
const nomeClasse = `${paraPascalCase(descreverEstruturaModulo(modulo.nome).nomeArquivo)}Service`;
|
|
630
|
+
const metodos = [
|
|
631
|
+
`class ${nomeClasse}:`,
|
|
632
|
+
...(modulo.tasks.length === 0
|
|
633
|
+
? [" pass"]
|
|
634
|
+
: modulo.tasks.flatMap((task) => [
|
|
635
|
+
` def ${normalizarNomeParaSimbolo(task.nome)}(self, entrada: ${task.nome}Entrada) -> ${task.nome}Saida:`,
|
|
636
|
+
...(task.implementacoesExternas.length > 0
|
|
637
|
+
? task.implementacoesExternas.map((impl) => ` # impl ${impl.origem}: ${impl.caminho}`)
|
|
638
|
+
: [" # TODO: conectar a implementacao real do projeto."]),
|
|
639
|
+
` return executar_${normalizarNomeParaSimbolo(task.nome)}(entrada)`,
|
|
640
|
+
"",
|
|
641
|
+
])),
|
|
642
|
+
];
|
|
643
|
+
return [`from ${caminhoContrato} import *`, "", ...metodos].join("\n");
|
|
644
|
+
}
|
|
645
|
+
function gerarFastApiRouter(modulo, caminhoSchemas, caminhoService) {
|
|
646
|
+
const nomeClasse = `${paraPascalCase(descreverEstruturaModulo(modulo.nome).nomeArquivo)}Service`;
|
|
647
|
+
const imports = [
|
|
648
|
+
"from fastapi import APIRouter",
|
|
649
|
+
`from ${caminhoSchemas} import *`,
|
|
650
|
+
`from ${caminhoService} import ${nomeClasse}`,
|
|
651
|
+
"",
|
|
652
|
+
"router = APIRouter()",
|
|
653
|
+
`service = ${nomeClasse}()`,
|
|
654
|
+
"",
|
|
655
|
+
];
|
|
656
|
+
const rotas = modulo.routes
|
|
657
|
+
.filter((route) => route.task)
|
|
658
|
+
.map((route) => {
|
|
659
|
+
const metodo = (route.metodo ?? "post").toLowerCase();
|
|
660
|
+
const schemaEntrada = `${route.nome}EntradaPublicaSchema`;
|
|
661
|
+
return `@router.${metodo}(${JSON.stringify(route.caminho ?? "/")})
|
|
662
|
+
def ${normalizarNomeParaSimbolo(route.nome)}(entrada: ${schemaEntrada}):
|
|
663
|
+
return adaptar_${normalizarNomeParaSimbolo(route.nome)}(${route.inputPublico.length > 0 ? `entrada` : `${schemaEntrada}()`})`;
|
|
664
|
+
}).join("\n\n");
|
|
665
|
+
return `${imports.join("\n")}${rotas}\n`;
|
|
666
|
+
}
|
|
667
|
+
function gerarFastApiTests(modulo, caminhoRouter) {
|
|
668
|
+
return `from fastapi.testclient import TestClient
|
|
669
|
+
from ${caminhoRouter} import router
|
|
670
|
+
from fastapi import FastAPI
|
|
671
|
+
|
|
672
|
+
app = FastAPI()
|
|
673
|
+
app.include_router(router)
|
|
674
|
+
client = TestClient(app)
|
|
675
|
+
|
|
676
|
+
def test_scaffold_${normalizarNomeParaSimbolo(descreverEstruturaModulo(modulo.nome).nomeArquivo)}() -> None:
|
|
677
|
+
assert client is not None
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
function gerarPythonFastApi(modulo) {
|
|
681
|
+
const base = gerarPythonBase(modulo);
|
|
682
|
+
const contrato = base.find((arquivo) => arquivo.caminhoRelativo.endsWith(".py") && !path.posix.basename(arquivo.caminhoRelativo).startsWith("test_"));
|
|
683
|
+
const testeContrato = base.find((arquivo) => path.posix.basename(arquivo.caminhoRelativo).startsWith("test_"));
|
|
684
|
+
const estrutura = descreverEstruturaModulo(modulo.nome);
|
|
685
|
+
const contexto = estrutura.contextoRelativo;
|
|
686
|
+
const contratoPath = `${contexto ? `${contexto}/` : ""}${estrutura.nomeArquivo}_contract.py`;
|
|
687
|
+
const schemasPath = `${contexto ? `${contexto}/` : ""}${estrutura.nomeArquivo}_schemas.py`;
|
|
688
|
+
const servicePath = `${contexto ? `${contexto}/` : ""}${estrutura.nomeArquivo}_service.py`;
|
|
689
|
+
const routerPath = `${contexto ? `${contexto}/` : ""}${estrutura.nomeArquivo}_router.py`;
|
|
690
|
+
const testContractPath = path.posix.join("tests", `${contexto ? `${contexto}/` : ""}test_${estrutura.nomeArquivo}_contract.py`);
|
|
691
|
+
const testRouterPath = path.posix.join("tests", `${contexto ? `${contexto}/` : ""}test_${estrutura.nomeArquivo}_router.py`);
|
|
692
|
+
const contratoModulo = path.posix.basename(contratoPath, ".py");
|
|
693
|
+
const schemasModulo = path.posix.basename(schemasPath, ".py");
|
|
694
|
+
const serviceModulo = path.posix.basename(servicePath, ".py");
|
|
695
|
+
const routerModulo = path.posix.basename(routerPath, ".py");
|
|
696
|
+
return [
|
|
697
|
+
{
|
|
698
|
+
caminhoRelativo: path.posix.join("app", contratoPath),
|
|
699
|
+
conteudo: contrato?.conteudo ?? "# Nenhum contrato base gerado.\n",
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
caminhoRelativo: path.posix.join("app", schemasPath),
|
|
703
|
+
conteudo: gerarFastApiSchemas(modulo, `.${contratoModulo}`),
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
caminhoRelativo: path.posix.join("app", servicePath),
|
|
707
|
+
conteudo: gerarFastApiService(modulo, `.${contratoModulo}`),
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
caminhoRelativo: path.posix.join("app", routerPath),
|
|
711
|
+
conteudo: gerarFastApiRouter(modulo, `.${schemasModulo}`, `.${serviceModulo}`),
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
caminhoRelativo: testContractPath,
|
|
715
|
+
conteudo: (testeContrato?.conteudo ?? "").replace(`from ${estrutura.nomeBase} import *`, `from app.${(contexto ? `${contexto.replace(/\//g, ".")}.` : "")}${contratoModulo} import *`),
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
caminhoRelativo: testRouterPath,
|
|
719
|
+
conteudo: gerarFastApiTests(modulo, `app.${(contexto ? `${contexto.replace(/\//g, ".")}.` : "")}${routerModulo}`),
|
|
720
|
+
},
|
|
721
|
+
];
|
|
722
|
+
}
|
|
723
|
+
export function gerarPython(modulo, opcoes = {}) {
|
|
724
|
+
if (opcoes.framework === "fastapi") {
|
|
725
|
+
return gerarPythonFastApi(modulo);
|
|
726
|
+
}
|
|
727
|
+
return gerarPythonBase(modulo);
|
|
728
|
+
}
|
|
729
|
+
//# sourceMappingURL=index.js.map
|