@semacode/cli 0.8.2

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.
Files changed (93) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +52 -0
  3. package/dist/cpp-symbols.d.ts +10 -0
  4. package/dist/cpp-symbols.js +71 -0
  5. package/dist/cpp-symbols.js.map +1 -0
  6. package/dist/dotnet-http.d.ts +23 -0
  7. package/dist/dotnet-http.js +301 -0
  8. package/dist/dotnet-http.js.map +1 -0
  9. package/dist/drift.d.ts +74 -0
  10. package/dist/drift.js +878 -0
  11. package/dist/drift.js.map +1 -0
  12. package/dist/go-http.d.ts +23 -0
  13. package/dist/go-http.js +90 -0
  14. package/dist/go-http.js.map +1 -0
  15. package/dist/importador.d.ts +29 -0
  16. package/dist/importador.js +2094 -0
  17. package/dist/importador.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +2150 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/java-http.d.ts +23 -0
  22. package/dist/java-http.js +204 -0
  23. package/dist/java-http.js.map +1 -0
  24. package/dist/projeto.d.ts +48 -0
  25. package/dist/projeto.js +560 -0
  26. package/dist/projeto.js.map +1 -0
  27. package/dist/python-http.d.ts +23 -0
  28. package/dist/python-http.js +200 -0
  29. package/dist/python-http.js.map +1 -0
  30. package/dist/rust-http.d.ts +23 -0
  31. package/dist/rust-http.js +95 -0
  32. package/dist/rust-http.js.map +1 -0
  33. package/dist/tipos.d.ts +3 -0
  34. package/dist/tipos.js +2 -0
  35. package/dist/tipos.js.map +1 -0
  36. package/dist/typescript-http.d.ts +35 -0
  37. package/dist/typescript-http.js +854 -0
  38. package/dist/typescript-http.js.map +1 -0
  39. package/logo.png +0 -0
  40. package/node_modules/@sema/gerador-dart/dist/index.d.ts +3 -0
  41. package/node_modules/@sema/gerador-dart/dist/index.js +44 -0
  42. package/node_modules/@sema/gerador-dart/dist/index.js.map +1 -0
  43. package/node_modules/@sema/gerador-dart/package.json +7 -0
  44. package/node_modules/@sema/gerador-python/dist/index.d.ts +6 -0
  45. package/node_modules/@sema/gerador-python/dist/index.js +510 -0
  46. package/node_modules/@sema/gerador-python/dist/index.js.map +1 -0
  47. package/node_modules/@sema/gerador-python/package.json +7 -0
  48. package/node_modules/@sema/gerador-typescript/dist/index.d.ts +6 -0
  49. package/node_modules/@sema/gerador-typescript/dist/index.js +646 -0
  50. package/node_modules/@sema/gerador-typescript/dist/index.js.map +1 -0
  51. package/node_modules/@sema/gerador-typescript/package.json +7 -0
  52. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +103 -0
  53. package/node_modules/@sema/nucleo/dist/ast/tipos.js +2 -0
  54. package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +1 -0
  55. package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +21 -0
  56. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +12 -0
  57. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +1 -0
  58. package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +9 -0
  59. package/node_modules/@sema/nucleo/dist/formatador/index.js +289 -0
  60. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -0
  61. package/node_modules/@sema/nucleo/dist/index.d.ts +34 -0
  62. package/node_modules/@sema/nucleo/dist/index.js +95 -0
  63. package/node_modules/@sema/nucleo/dist/index.js.map +1 -0
  64. package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +5 -0
  65. package/node_modules/@sema/nucleo/dist/ir/conversor.js +241 -0
  66. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -0
  67. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +131 -0
  68. package/node_modules/@sema/nucleo/dist/ir/modelos.js +2 -0
  69. package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +1 -0
  70. package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +7 -0
  71. package/node_modules/@sema/nucleo/dist/lexer/lexer.js +122 -0
  72. package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +1 -0
  73. package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +8 -0
  74. package/node_modules/@sema/nucleo/dist/lexer/tokens.js +30 -0
  75. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -0
  76. package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +9 -0
  77. package/node_modules/@sema/nucleo/dist/parser/parser.js +423 -0
  78. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -0
  79. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +52 -0
  80. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +837 -0
  81. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -0
  82. package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +99 -0
  83. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +395 -0
  84. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +1 -0
  85. package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +2 -0
  86. package/node_modules/@sema/nucleo/dist/util/arquivos.js +25 -0
  87. package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +1 -0
  88. package/node_modules/@sema/nucleo/package.json +7 -0
  89. package/node_modules/@sema/padroes/dist/index.d.ts +20 -0
  90. package/node_modules/@sema/padroes/dist/index.js +79 -0
  91. package/node_modules/@sema/padroes/dist/index.js.map +1 -0
  92. package/node_modules/@sema/padroes/package.json +7 -0
  93. package/package.json +57 -0
@@ -0,0 +1,837 @@
1
+ import { criarDiagnostico } from "../diagnosticos/index.js";
2
+ import { ehCategoriaEfeitoSemantico, ehCriticidadeEfeitoSemantico, extrairReferenciasDaExpressao, parsearEfeitoSemantico, parsearEtapaFlow, parsearExpressaoSemantica, parsearTransicaoEstado, } from "./estruturas.js";
3
+ function ehUseInterop(use) {
4
+ return use.origem !== "sema";
5
+ }
6
+ const TIPOS_PRIMITIVOS = new Set([
7
+ "Texto",
8
+ "Numero",
9
+ "Inteiro",
10
+ "Decimal",
11
+ "Booleano",
12
+ "Data",
13
+ "DataHora",
14
+ "Id",
15
+ "Email",
16
+ "Url",
17
+ "Json",
18
+ "Vazio",
19
+ ]);
20
+ const PADRAO_CAMINHO_INTEROP = /^[A-Za-z_][A-Za-z0-9_-]*(\.[A-Za-z_][A-Za-z0-9_-]*)*$/;
21
+ function normalizarOrigemImplementacao(valor) {
22
+ switch (valor.toLowerCase()) {
23
+ case "ts":
24
+ case "typescript":
25
+ return "ts";
26
+ case "py":
27
+ case "python":
28
+ return "py";
29
+ case "dart":
30
+ return "dart";
31
+ case "cs":
32
+ case "csharp":
33
+ case "dotnet":
34
+ return "cs";
35
+ case "java":
36
+ return "java";
37
+ case "go":
38
+ case "golang":
39
+ return "go";
40
+ case "rust":
41
+ case "rs":
42
+ return "rust";
43
+ case "cpp":
44
+ case "cxx":
45
+ case "cc":
46
+ case "c++":
47
+ return "cpp";
48
+ default:
49
+ return undefined;
50
+ }
51
+ }
52
+ function extrairReferenciasDeTipos(texto) {
53
+ const correspondencias = texto.match(/[A-Z][A-Za-z0-9_]*/g);
54
+ return correspondencias ?? [];
55
+ }
56
+ function extrairRaiz(referencia) {
57
+ return referencia.split(".")[0] ?? referencia;
58
+ }
59
+ function ehMarcadorSemantico(referencia) {
60
+ return ["persistencia", "sucesso", "estado"].includes(extrairRaiz(referencia));
61
+ }
62
+ function diagnosticoDuplicado(nome, categoria, intervalo) {
63
+ return criarDiagnostico("SEM001", `${categoria} "${nome}" foi declarado mais de uma vez no mesmo modulo.`, "erro", intervalo, "Use nomes unicos para simbolos do modulo.");
64
+ }
65
+ function validarCamposDeTipos(campos, tiposConhecidos, diagnosticos, contexto) {
66
+ for (const campo of campos) {
67
+ const referencias = extrairReferenciasDeTipos(campo.valor);
68
+ for (const referencia of referencias) {
69
+ if (!tiposConhecidos.has(referencia)) {
70
+ diagnosticos.push(criarDiagnostico("SEM002", `Tipo "${referencia}" nao foi encontrado em ${contexto}.`, "erro", campo.intervalo, "Declare o tipo, entidade ou enum antes de usa-lo."));
71
+ }
72
+ }
73
+ }
74
+ }
75
+ function localizarBloco(corpo, nome) {
76
+ return corpo.blocos.find((bloco) => bloco.tipo === "bloco_generico" && bloco.palavraChave === nome);
77
+ }
78
+ function localizarCampo(bloco, ...nomes) {
79
+ return bloco.campos.find((campo) => nomes.includes(campo.nome));
80
+ }
81
+ function converterCampoSemantico(campo) {
82
+ return {
83
+ nome: campo.nome,
84
+ tipo: campo.valor,
85
+ modificadores: [...campo.modificadores],
86
+ };
87
+ }
88
+ function indicesCampos(campos) {
89
+ return new Map(campos.map((campo) => [campo.nome, campo]));
90
+ }
91
+ function indiceErros(erros) {
92
+ return new Map(erros.map((erro) => [erro.codigo, erro]));
93
+ }
94
+ function coletarErrosTask(task) {
95
+ const erros = new Map();
96
+ for (const campo of task.error?.campos ?? []) {
97
+ erros.set(campo.nome, {
98
+ codigo: campo.nome,
99
+ mensagem: [campo.valor, ...campo.modificadores].join(" ").trim(),
100
+ });
101
+ }
102
+ for (const bloco of task.tests?.blocos ?? []) {
103
+ if (bloco.tipo !== "caso_teste") {
104
+ continue;
105
+ }
106
+ const codigoErro = bloco.error?.campos.find((campo) => campo.nome === "tipo")?.valor;
107
+ if (codigoErro && !erros.has(codigoErro)) {
108
+ erros.set(codigoErro, {
109
+ codigo: codigoErro,
110
+ mensagem: `Erro sintetico derivado do caso de teste "${bloco.nome}".`,
111
+ });
112
+ }
113
+ }
114
+ return [...erros.values()];
115
+ }
116
+ function coletarResumoTask(task) {
117
+ return {
118
+ input: (task.input?.campos ?? []).map(converterCampoSemantico),
119
+ output: (task.output?.campos ?? []).map(converterCampoSemantico),
120
+ errors: coletarErrosTask(task),
121
+ guarantees: (task.guarantees?.linhas ?? []).map((linha) => linha.conteudo),
122
+ implementacoes: (task.impl?.campos ?? [])
123
+ .map((campo) => {
124
+ const origem = normalizarOrigemImplementacao(campo.nome);
125
+ return origem ? { origem, caminho: campo.valor } : undefined;
126
+ })
127
+ .filter((item) => Boolean(item)),
128
+ };
129
+ }
130
+ function validarImplementacoesTask(task, diagnosticos) {
131
+ if (!task.impl) {
132
+ return;
133
+ }
134
+ const origens = new Set();
135
+ for (const campo of task.impl.campos) {
136
+ const origem = normalizarOrigemImplementacao(campo.nome);
137
+ if (!origem) {
138
+ diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use apenas ts, py, dart, cs, java, go, rust ou cpp dentro do bloco impl."));
139
+ continue;
140
+ }
141
+ if (origens.has(origem)) {
142
+ diagnosticos.push(criarDiagnostico("SEM060", `Task "${task.nome}" declarou mais de uma implementacao ${origem} no bloco impl.`, "erro", campo.intervalo, "Cada origem externa deve aparecer no maximo uma vez dentro de impl."));
143
+ continue;
144
+ }
145
+ origens.add(origem);
146
+ if (!PADRAO_CAMINHO_INTEROP.test(campo.valor)) {
147
+ diagnosticos.push(criarDiagnostico("SEM061", `Task "${task.nome}" declarou caminho invalido para impl ${origem}: "${campo.valor}".`, "erro", campo.intervalo, "Use um identificador de implementacao como pacote.modulo.funcao ou app.servico.metodo."));
148
+ }
149
+ }
150
+ }
151
+ function recomporCaminhoRoute(campo) {
152
+ if (!campo) {
153
+ return undefined;
154
+ }
155
+ return [campo.valor, ...campo.modificadores]
156
+ .join(" ")
157
+ .replace(/\s*\/\s*/g, "/")
158
+ .trim();
159
+ }
160
+ function serializarTransicao(origem, destino) {
161
+ return `${origem}->${destino}`;
162
+ }
163
+ function descreverSugestoes(valores, prefixo) {
164
+ const lista = [...new Set([...valores].filter(Boolean))].sort((a, b) => a.localeCompare(b, "pt-BR"));
165
+ if (lista.length === 0) {
166
+ return undefined;
167
+ }
168
+ const recorte = lista.slice(0, 6).join(", ");
169
+ return `${prefixo}: ${recorte}${lista.length > 6 ? ", ..." : ""}.`;
170
+ }
171
+ function listarCandidatosUseRelativo(moduloAtual, caminhoImportado) {
172
+ const segmentos = moduloAtual.split(".").filter(Boolean);
173
+ const caminhoNormalizado = caminhoImportado.replace(/^\.+/u, "").trim();
174
+ if (!caminhoNormalizado || segmentos.length <= 1) {
175
+ return [];
176
+ }
177
+ const candidatos = [];
178
+ for (let tamanho = segmentos.length - 1; tamanho >= 1; tamanho -= 1) {
179
+ const candidato = [...segmentos.slice(0, tamanho), caminhoNormalizado].join(".");
180
+ if (candidato !== caminhoImportado) {
181
+ candidatos.push(candidato);
182
+ }
183
+ }
184
+ return [...new Set(candidatos)];
185
+ }
186
+ function resolverUseSema(moduloAtual, caminhoImportado, contextosModulos) {
187
+ if (!contextosModulos) {
188
+ return { caminhoResolvido: undefined, candidatosRelativos: [] };
189
+ }
190
+ if (contextosModulos.has(caminhoImportado)) {
191
+ return { caminhoResolvido: caminhoImportado, candidatosRelativos: [] };
192
+ }
193
+ const candidatosRelativos = listarCandidatosUseRelativo(moduloAtual, caminhoImportado);
194
+ const caminhoResolvido = candidatosRelativos.find((candidato) => contextosModulos.has(candidato));
195
+ return { caminhoResolvido, candidatosRelativos };
196
+ }
197
+ function descreverDicaSintaxeExpressao(texto, dicaPadrao) {
198
+ const normalizado = texto.trim();
199
+ if (/\sou\s/u.test(normalizado)
200
+ && (normalizado.includes("\"")
201
+ || normalizado.includes("'")
202
+ || /^[A-Za-z_][A-Za-z0-9_.]*\s*(==|!=|>|<|>=|<=)\s+.+\s+ou\s+.+$/u.test(normalizado))
203
+ && !/\bem\s+\[/u.test(normalizado)) {
204
+ const alvo = normalizado.match(/^([A-Za-z_][A-Za-z0-9_.]*)\s*(==|!=|>|<|>=|<=)/u)?.[1];
205
+ if (alvo) {
206
+ return `${dicaPadrao} Se a ideia era comparar ${alvo} contra varios valores, repita o campo em cada comparacao ou prefira "${alvo} em [A, B]".`;
207
+ }
208
+ return `${dicaPadrao} Se a ideia era comparar um campo contra varios valores, repita o campo em cada comparacao ou prefira "campo em [A, B]".`;
209
+ }
210
+ return dicaPadrao;
211
+ }
212
+ function validarExpressoesDeclaradas(linhas, diagnosticos, contexto) {
213
+ for (const linha of linhas) {
214
+ const expressao = parsearExpressaoSemantica(linha.conteudo);
215
+ if (!expressao) {
216
+ diagnosticos.push(criarDiagnostico(contexto.codigoErroSintaxe, `Declaracao invalida em ${contexto.nomeBloco}: "${linha.conteudo}".`, "erro", linha.intervalo, descreverDicaSintaxeExpressao(linha.conteudo, contexto.dicaSintaxe)));
217
+ continue;
218
+ }
219
+ for (const referencia of extrairReferenciasDaExpressao(expressao)) {
220
+ const raiz = extrairRaiz(referencia);
221
+ const referenciaPermitida = contexto.simbolosPermitidos.has(raiz) || (contexto.aceitarMarcadoresSemanticos && ehMarcadorSemantico(raiz));
222
+ if (!referenciaPermitida) {
223
+ diagnosticos.push(criarDiagnostico(contexto.codigoErroReferencia, `Declaracao em ${contexto.nomeBloco} referencia "${raiz}", que nao pertence ao contexto permitido.`, "erro", linha.intervalo, contexto.dicaReferenciaPersonalizada?.(raiz, linha.conteudo) ?? contexto.dicaReferencia));
224
+ }
225
+ }
226
+ }
227
+ }
228
+ function validarEfeitosDeclarados(linhas, diagnosticos, contexto) {
229
+ for (const linha of linhas) {
230
+ const efeito = parsearEfeitoSemantico(linha.conteudo);
231
+ if (!efeito) {
232
+ diagnosticos.push(criarDiagnostico("SEM023", `Declaracao invalida de efeito em ${contexto}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"categoria alvo\" ou \"categoria alvo detalhe\", com categorias como persistencia, consulta, evento, notificacao ou auditoria."));
233
+ continue;
234
+ }
235
+ if (!ehCategoriaEfeitoSemantico(efeito.categoria)) {
236
+ diagnosticos.push(criarDiagnostico("SEM048", `Categoria de efeito "${efeito.categoria}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use apenas persistencia, consulta, evento, notificacao ou auditoria."));
237
+ }
238
+ if (efeito.criticidadeTexto && !ehCriticidadeEfeitoSemantico(efeito.criticidadeTexto)) {
239
+ diagnosticos.push(criarDiagnostico("SEM052", `Criticidade de efeito "${efeito.criticidadeTexto}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use apenas criticidade=baixa, criticidade=media, criticidade=alta ou criticidade=critica."));
240
+ }
241
+ }
242
+ }
243
+ function validarState(state, tiposConhecidos, enumsConhecidos, diagnosticos) {
244
+ const possuiConteudo = state.corpo.campos.length > 0 || state.corpo.linhas.length > 0 || state.corpo.blocos.length > 0;
245
+ if (!possuiConteudo) {
246
+ diagnosticos.push(criarDiagnostico("SEM011", `Bloco state${state.nome ? ` "${state.nome}"` : ""} precisa declarar campos, linhas ou subblocos.`, "erro", state.intervalo, "Use state para modelar informacao ou transicao observavel, nao como bloco vazio."));
247
+ }
248
+ validarCamposDeTipos(state.corpo.campos, tiposConhecidos, diagnosticos, `state ${state.nome ?? "<anonimo>"}`);
249
+ const fields = localizarBloco(state.corpo, "fields");
250
+ if (fields) {
251
+ validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `fields do state ${state.nome ?? "<anonimo>"}`);
252
+ }
253
+ const nomesCampos = new Set([
254
+ ...state.corpo.campos.map((campo) => campo.nome),
255
+ ...(fields?.campos ?? []).map((campo) => campo.nome),
256
+ ]);
257
+ const invariants = localizarBloco(state.corpo, "invariants");
258
+ if (invariants) {
259
+ validarExpressoesDeclaradas(invariants.linhas, diagnosticos, {
260
+ codigoErroSintaxe: "SEM024",
261
+ codigoErroReferencia: "SEM025",
262
+ nomeBloco: `invariants do state ${state.nome ?? "<anonimo>"}`,
263
+ simbolosPermitidos: nomesCampos,
264
+ dicaSintaxe: "Use expressoes como \"campo existe\", \"campo == valor\" ou \"campo em [A, B]\".",
265
+ dicaReferencia: "Referencie apenas campos do proprio state nas invariantes.",
266
+ });
267
+ }
268
+ const transitions = localizarBloco(state.corpo, "transitions");
269
+ if (transitions) {
270
+ const campoTransicao = (fields?.campos ?? []).find((campo) => campo.nome === "status" || campo.nome === "estado")
271
+ ?? state.corpo.campos.find((campo) => campo.nome === "status" || campo.nome === "estado");
272
+ if (!campoTransicao) {
273
+ diagnosticos.push(criarDiagnostico("SEM026", `State ${state.nome ? `"${state.nome}" ` : ""}declarou transitions sem um campo status ou estado.`, "erro", transitions.intervalo, "Adicione um campo status ou estado para ancorar semanticamente as transicoes."));
274
+ }
275
+ const enumValores = campoTransicao ? enumsConhecidos.get(campoTransicao.valor) : undefined;
276
+ for (const linha of transitions.linhas) {
277
+ const transicao = parsearTransicaoEstado(linha.conteudo);
278
+ if (!transicao) {
279
+ diagnosticos.push(criarDiagnostico("SEM027", `Transicao invalida em state ${state.nome ?? "<anonimo>"}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"ORIGEM -> DESTINO\" para declarar transicoes."));
280
+ continue;
281
+ }
282
+ if (enumValores) {
283
+ if (!enumValores.has(transicao.origem)) {
284
+ diagnosticos.push(criarDiagnostico("SEM028", `Transicao do state ${state.nome ?? "<anonimo>"} usa origem "${transicao.origem}" fora do enum ${campoTransicao?.valor}.`, "erro", linha.intervalo, "Use apenas valores declarados no enum associado ao campo status/estado."));
285
+ }
286
+ if (!enumValores.has(transicao.destino)) {
287
+ diagnosticos.push(criarDiagnostico("SEM029", `Transicao do state ${state.nome ?? "<anonimo>"} usa destino "${transicao.destino}" fora do enum ${campoTransicao?.valor}.`, "erro", linha.intervalo, "Use apenas valores declarados no enum associado ao campo status/estado."));
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ function validarInvariantesDeCampos(bloco, nomeBloco, diagnosticos) {
294
+ const fields = localizarBloco(bloco, "fields");
295
+ const nomesCampos = new Set([
296
+ ...bloco.campos.map((campo) => campo.nome),
297
+ ...(fields?.campos ?? []).map((campo) => campo.nome),
298
+ ]);
299
+ const invariants = localizarBloco(bloco, "invariants");
300
+ if (!invariants) {
301
+ return;
302
+ }
303
+ validarExpressoesDeclaradas(invariants.linhas, diagnosticos, {
304
+ codigoErroSintaxe: "SEM062",
305
+ codigoErroReferencia: "SEM063",
306
+ nomeBloco: `invariants de ${nomeBloco}`,
307
+ simbolosPermitidos: nomesCampos,
308
+ dicaSintaxe: "Use expressoes como \"campo existe\", \"campo == valor\" ou \"campo em [A, B]\".",
309
+ dicaReferencia: "Referencie apenas campos declarados no proprio bloco ao escrever invariantes de dominio.",
310
+ });
311
+ }
312
+ function validarFlow(flow, tasksConhecidas, tarefasDetalhadas, diagnosticos) {
313
+ const possuiEtapas = flow.corpo.linhas.length > 0 || flow.corpo.campos.length > 0 || flow.corpo.blocos.length > 0;
314
+ if (!possuiEtapas) {
315
+ diagnosticos.push(criarDiagnostico("SEM012", `Flow "${flow.nome}" precisa declarar ao menos uma etapa.`, "erro", flow.intervalo, "Adicione linhas declarativas, campos ou subblocos dentro de flow."));
316
+ }
317
+ const effects = localizarBloco(flow.corpo, "effects");
318
+ if (effects) {
319
+ validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects do flow ${flow.nome}`);
320
+ }
321
+ const tarefasReferenciadas = flow.corpo.campos
322
+ .filter((campo) => campo.nome === "task" || campo.nome === "tarefa")
323
+ .map((campo) => campo.valor);
324
+ for (const tarefa of tarefasReferenciadas) {
325
+ if (!tasksConhecidas.has(tarefa)) {
326
+ diagnosticos.push(criarDiagnostico("SEM013", `Flow "${flow.nome}" referencia task "${tarefa}" que nao existe.`, "erro", flow.intervalo, "Declare a task no mesmo modulo ou ajuste a referencia do flow."));
327
+ }
328
+ }
329
+ const etapas = flow.corpo.linhas
330
+ .map((linha) => ({ linha, etapa: parsearEtapaFlow(linha.conteudo) }))
331
+ .filter((item) => item.linha.conteudo.trim().startsWith("etapa "));
332
+ const nomesEtapas = new Set();
333
+ const contextoFlow = new Set(flow.corpo.campos.map((campo) => campo.nome));
334
+ const etapasValidas = new Map();
335
+ for (const item of etapas) {
336
+ if (!item.etapa) {
337
+ diagnosticos.push(criarDiagnostico("SEM032", `Linha de etapa invalida em flow "${flow.nome}": "${item.linha.conteudo}".`, "erro", item.linha.intervalo, "Use o formato \"etapa nome usa task com campo=valor quando expressao depende_de etapa_a, etapa_b em_sucesso proxima em_erro falha\"."));
338
+ continue;
339
+ }
340
+ if (nomesEtapas.has(item.etapa.nome)) {
341
+ diagnosticos.push(criarDiagnostico("SEM033", `Flow "${flow.nome}" declarou a etapa "${item.etapa.nome}" mais de uma vez.`, "erro", item.linha.intervalo, "Use nomes unicos para cada etapa estruturada do flow."));
342
+ continue;
343
+ }
344
+ nomesEtapas.add(item.etapa.nome);
345
+ etapasValidas.set(item.etapa.nome, item.etapa);
346
+ if (item.etapa.task && !tasksConhecidas.has(item.etapa.task)) {
347
+ const sugestoesTasks = descreverSugestoes(tasksConhecidas, "Tasks conhecidas no contexto");
348
+ diagnosticos.push(criarDiagnostico("SEM034", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" usa task "${item.etapa.task}" que nao existe.`, "erro", item.linha.intervalo, sugestoesTasks ?? "Ajuste a task da etapa para apontar para uma task declarada ou importada."));
349
+ }
350
+ if (item.etapa.task) {
351
+ const detalhesTask = tarefasDetalhadas.get(item.etapa.task);
352
+ if (detalhesTask) {
353
+ const indiceInput = indicesCampos(detalhesTask.input);
354
+ for (const mapeamento of item.etapa.mapeamentos) {
355
+ const campoInput = indiceInput.get(mapeamento.campo);
356
+ if (!campoInput) {
357
+ diagnosticos.push(criarDiagnostico("SEM042", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" mapeia o campo "${mapeamento.campo}", que nao existe no input da task "${item.etapa.task}".`, "erro", item.linha.intervalo, "Use apenas campos declarados no input da task associada a etapa."));
358
+ }
359
+ const raizValor = extrairRaiz(mapeamento.valor);
360
+ const ehLiteral = ["verdadeiro", "falso", "nulo"].includes(mapeamento.valor)
361
+ || /^-?\d+(?:\.\d+)?$/.test(mapeamento.valor)
362
+ || /^".*"$/.test(mapeamento.valor);
363
+ const aceitaLiteralTextual = Boolean(campoInput
364
+ && ["Texto", "Id", "Email", "Url"].includes(campoInput.tipo)
365
+ && !mapeamento.valor.includes(".")
366
+ && !contextoFlow.has(raizValor)
367
+ && !nomesEtapas.has(raizValor));
368
+ if (!ehLiteral && !aceitaLiteralTextual && !contextoFlow.has(raizValor) && !nomesEtapas.has(raizValor)) {
369
+ const sugestoesContexto = [
370
+ descreverSugestoes(contextoFlow, "Campos do flow"),
371
+ descreverSugestoes(nomesEtapas, "Etapas conhecidas"),
372
+ ].filter(Boolean).join(" ");
373
+ diagnosticos.push(criarDiagnostico("SEM043", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" referencia "${mapeamento.valor}" fora do contexto conhecido.`, "erro", item.linha.intervalo, sugestoesContexto || "Mapeie usando campos do proprio flow, saidas de etapas anteriores ou literais simples."));
374
+ }
375
+ if (mapeamento.valor.includes(".")) {
376
+ const [etapaOrigem, campoSaida] = mapeamento.valor.split(".", 2);
377
+ const etapaReferenciada = etapaOrigem ? etapasValidas.get(etapaOrigem) : undefined;
378
+ const taskReferenciada = etapaReferenciada?.task ? tarefasDetalhadas.get(etapaReferenciada.task) : undefined;
379
+ const indiceOutput = taskReferenciada ? indicesCampos(taskReferenciada.output) : undefined;
380
+ if (etapaOrigem && campoSaida && etapaReferenciada && indiceOutput && !indiceOutput.has(campoSaida)) {
381
+ diagnosticos.push(criarDiagnostico("SEM044", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" referencia a saida "${campoSaida}" da etapa "${etapaOrigem}", mas essa saida nao existe na task associada.`, "erro", item.linha.intervalo, "Use apenas campos declarados no output da task da etapa de origem."));
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+ if (item.etapa.condicao) {
388
+ const referencias = extrairReferenciasDaExpressao(item.etapa.condicao).map((referencia) => extrairRaiz(referencia));
389
+ for (const referencia of referencias) {
390
+ if (!ehMarcadorSemantico(referencia) && !tasksConhecidas.has(referencia) && !nomesEtapas.has(referencia) && !contextoFlow.has(referencia)) {
391
+ diagnosticos.push(criarDiagnostico("SEM035", `Condicao da etapa "${item.etapa.nome}" em flow "${flow.nome}" referencia "${referencia}" fora do contexto atual.`, "erro", item.linha.intervalo, "No MVP atual, condicoes de flow devem apontar para marcadores semanticos, campos do flow, tasks conhecidas ou etapas anteriores."));
392
+ }
393
+ }
394
+ }
395
+ }
396
+ for (const item of etapas) {
397
+ if (!item.etapa) {
398
+ continue;
399
+ }
400
+ for (const dependencia of item.etapa.dependencias) {
401
+ if (!nomesEtapas.has(dependencia)) {
402
+ const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
403
+ diagnosticos.push(criarDiagnostico("SEM036", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" depende de "${dependencia}", que nao foi declarada.`, "erro", item.linha.intervalo, sugestoesEtapas ?? "Declare a etapa dependente no mesmo flow antes de referencia-la."));
404
+ }
405
+ }
406
+ for (const destino of [item.etapa.emSucesso, item.etapa.emErro].filter(Boolean)) {
407
+ if (destino && !nomesEtapas.has(destino)) {
408
+ const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
409
+ diagnosticos.push(criarDiagnostico("SEM045", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" aponta para "${destino}" em ramificacao, mas essa etapa nao foi declarada.`, "erro", item.linha.intervalo, sugestoesEtapas ?? "Declare a etapa de destino no mesmo flow antes de usa-la em em_sucesso ou em_erro."));
410
+ }
411
+ }
412
+ if (item.etapa.task) {
413
+ const detalhesTask = tarefasDetalhadas.get(item.etapa.task);
414
+ const indiceErrors = indiceErros(detalhesTask?.errors ?? []);
415
+ for (const rotaErro of item.etapa.porErro) {
416
+ if (!indiceErrors.has(rotaErro.tipo)) {
417
+ const sugestoesErros = descreverSugestoes(indiceErrors.keys(), "Erros declarados pela task");
418
+ diagnosticos.push(criarDiagnostico("SEM046", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" roteia o erro "${rotaErro.tipo}", mas esse erro nao pertence ao contrato da task "${item.etapa.task}".`, "erro", item.linha.intervalo, sugestoesErros ?? "Use apenas erros declarados pela task ou cobertos por testes de erro do contrato atual."));
419
+ }
420
+ if (!nomesEtapas.has(rotaErro.destino)) {
421
+ const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
422
+ diagnosticos.push(criarDiagnostico("SEM047", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" aponta o erro "${rotaErro.tipo}" para "${rotaErro.destino}", mas essa etapa nao foi declarada.`, "erro", item.linha.intervalo, sugestoesEtapas ?? "Declare a etapa de destino no mesmo flow antes de usa-la em por_erro."));
423
+ }
424
+ }
425
+ }
426
+ }
427
+ }
428
+ function validarVinculoEstadoDaTask(task, statesConhecidos, diagnosticos) {
429
+ if (!task.state) {
430
+ return;
431
+ }
432
+ const nomeEstado = task.state.nome ?? task.state.campos.find((campo) => campo.nome === "state" || campo.nome === "estado")?.valor;
433
+ if (!nomeEstado) {
434
+ diagnosticos.push(criarDiagnostico("SEM037", `Task "${task.nome}" declarou state sem indicar qual estado ela governa.`, "erro", task.state.intervalo, "Use \"state nome_do_estado { ... }\" ou declare \"estado: nome_do_estado\" dentro do bloco state da task."));
435
+ return;
436
+ }
437
+ const estadoConhecido = statesConhecidos.get(nomeEstado);
438
+ if (!estadoConhecido) {
439
+ diagnosticos.push(criarDiagnostico("SEM038", `Task "${task.nome}" referencia state "${nomeEstado}", mas esse state nao foi encontrado.`, "erro", task.state.intervalo, "Declare o state no mesmo modulo ou importe um modulo que exponha esse state."));
440
+ return;
441
+ }
442
+ const blocoTransitions = localizarBloco(task.state, "transitions");
443
+ const linhasTransicao = blocoTransitions?.linhas ?? task.state.linhas;
444
+ if (linhasTransicao.length === 0) {
445
+ diagnosticos.push(criarDiagnostico("SEM039", `Task "${task.nome}" referencia state "${nomeEstado}" sem declarar transicoes.`, "erro", task.state.intervalo, "Declare ao menos uma transicao para explicitar como a task altera o estado."));
446
+ return;
447
+ }
448
+ for (const linha of linhasTransicao) {
449
+ const transicao = parsearTransicaoEstado(linha.conteudo);
450
+ if (!transicao) {
451
+ diagnosticos.push(criarDiagnostico("SEM040", `Task "${task.nome}" declarou uma transicao invalida no state "${nomeEstado}": "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"ORIGEM -> DESTINO\" dentro do bloco transitions da task."));
452
+ continue;
453
+ }
454
+ if (!estadoConhecido.transicoes.has(serializarTransicao(transicao.origem, transicao.destino))) {
455
+ diagnosticos.push(criarDiagnostico("SEM041", `Task "${task.nome}" declarou a transicao "${transicao.origem} -> ${transicao.destino}" fora do contrato do state "${nomeEstado}".`, "erro", linha.intervalo, "Use apenas transicoes declaradas no state associado a task."));
456
+ }
457
+ }
458
+ }
459
+ function validarContratoRoute(route, taskNome, tarefasDetalhadas, diagnosticos) {
460
+ const inputPublico = localizarBloco(route.corpo, "input");
461
+ const outputPublico = localizarBloco(route.corpo, "output");
462
+ const errorPublico = localizarBloco(route.corpo, "error");
463
+ const detalhesTask = tarefasDetalhadas.get(taskNome);
464
+ if (!detalhesTask) {
465
+ return;
466
+ }
467
+ const indiceInputTask = indicesCampos(detalhesTask.input);
468
+ const indiceOutputTask = indicesCampos(detalhesTask.output);
469
+ const indiceErrorsTask = indiceErros(detalhesTask.errors);
470
+ if (inputPublico) {
471
+ for (const campo of inputPublico.campos) {
472
+ const campoTask = indiceInputTask.get(campo.nome);
473
+ if (!campoTask) {
474
+ diagnosticos.push(criarDiagnostico("SEM049", `Route "${route.nome}" expoe o campo de input "${campo.nome}", mas ele nao existe na task "${taskNome}".`, "erro", campo.intervalo, "Use apenas campos declarados no input da task associada a route."));
475
+ continue;
476
+ }
477
+ if (campoTask.tipo !== campo.valor) {
478
+ diagnosticos.push(criarDiagnostico("SEM053", `Route "${route.nome}" declara o campo publico "${campo.nome}" com tipo "${campo.valor}", mas a task "${taskNome}" usa "${campoTask.tipo}".`, "erro", campo.intervalo, "Mantenha o tipo publico coerente com o contrato interno da task."));
479
+ }
480
+ }
481
+ }
482
+ if (outputPublico) {
483
+ for (const campo of outputPublico.campos) {
484
+ const campoTask = indiceOutputTask.get(campo.nome);
485
+ if (!campoTask) {
486
+ diagnosticos.push(criarDiagnostico("SEM050", `Route "${route.nome}" expoe o campo de output "${campo.nome}", mas ele nao existe na task "${taskNome}".`, "erro", campo.intervalo, "Use apenas campos declarados no output da task associada a route."));
487
+ continue;
488
+ }
489
+ if (campoTask.tipo !== campo.valor) {
490
+ diagnosticos.push(criarDiagnostico("SEM054", `Route "${route.nome}" declara o campo publico de saida "${campo.nome}" com tipo "${campo.valor}", mas a task "${taskNome}" usa "${campoTask.tipo}".`, "erro", campo.intervalo, "Mantenha o output publico coerente com o contrato interno da task."));
491
+ }
492
+ }
493
+ }
494
+ if (errorPublico) {
495
+ for (const campo of errorPublico.campos) {
496
+ if (!indiceErrorsTask.has(campo.nome)) {
497
+ diagnosticos.push(criarDiagnostico("SEM051", `Route "${route.nome}" expoe o erro "${campo.nome}", mas ele nao pertence ao contrato da task "${taskNome}".`, "erro", campo.intervalo, "Exponha apenas erros declarados pela task associada a route."));
498
+ }
499
+ }
500
+ }
501
+ }
502
+ function validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos) {
503
+ const metodo = localizarCampo(route.corpo, "metodo");
504
+ const caminho = localizarCampo(route.corpo, "caminho");
505
+ const caminhoResolvido = recomporCaminhoRoute(caminho);
506
+ const task = localizarCampo(route.corpo, "task", "tarefa");
507
+ const effects = localizarBloco(route.corpo, "effects");
508
+ if (!metodo) {
509
+ diagnosticos.push(criarDiagnostico("SEM014", `Route "${route.nome}" precisa declarar o campo metodo.`, "erro", route.intervalo, "Use um campo como metodo: GET, POST, PUT, PATCH ou DELETE."));
510
+ }
511
+ if (!caminho) {
512
+ diagnosticos.push(criarDiagnostico("SEM015", `Route "${route.nome}" precisa declarar o campo caminho.`, "erro", route.intervalo, "Use um campo como caminho: \"/recurso\"."));
513
+ }
514
+ if (metodo) {
515
+ const metodosValidos = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
516
+ if (!metodosValidos.has(metodo.valor.toUpperCase())) {
517
+ diagnosticos.push(criarDiagnostico("SEM016", `Route "${route.nome}" usa metodo invalido "${metodo.valor}".`, "erro", metodo.intervalo, "Use apenas GET, POST, PUT, PATCH ou DELETE no MVP."));
518
+ }
519
+ }
520
+ if (caminho && (!caminhoResolvido || !caminhoResolvido.startsWith("/"))) {
521
+ diagnosticos.push(criarDiagnostico("SEM017", `Route "${route.nome}" precisa usar um caminho iniciando com '/'.`, "erro", caminho.intervalo, "Exemplo valido: caminho: \"/produtos\"."));
522
+ }
523
+ if (effects) {
524
+ validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects da route ${route.nome}`);
525
+ }
526
+ if (task && !tasksConhecidas.has(task.valor)) {
527
+ diagnosticos.push(criarDiagnostico("SEM018", `Route "${route.nome}" referencia task "${task.valor}" que nao existe.`, "erro", task.intervalo, "Ajuste o campo task da route para apontar para uma task declarada no modulo."));
528
+ return;
529
+ }
530
+ if (task) {
531
+ validarContratoRoute(route, task.valor, tarefasDetalhadas, diagnosticos);
532
+ }
533
+ }
534
+ export function criarContextoLocal(modulo) {
535
+ const simbolos = new Map();
536
+ const tiposConhecidos = new Set(TIPOS_PRIMITIVOS);
537
+ const tasksConhecidas = new Set();
538
+ const tarefasDetalhadas = new Map();
539
+ const statesConhecidos = new Map();
540
+ const enumsConhecidos = new Map();
541
+ const registrar = (nome, categoria) => {
542
+ if (simbolos.has(nome)) {
543
+ return;
544
+ }
545
+ simbolos.set(nome, { nome, categoria });
546
+ if (categoria === "task") {
547
+ tasksConhecidas.add(nome);
548
+ return;
549
+ }
550
+ tiposConhecidos.add(nome);
551
+ };
552
+ for (const type of modulo.types) {
553
+ registrar(type.nome, "tipo");
554
+ }
555
+ for (const entity of modulo.entities) {
556
+ registrar(entity.nome, "entity");
557
+ }
558
+ for (const enumeracao of modulo.enums) {
559
+ registrar(enumeracao.nome, "enum");
560
+ enumsConhecidos.set(enumeracao.nome, new Set(enumeracao.valores));
561
+ }
562
+ for (const task of modulo.tasks) {
563
+ registrar(task.nome, "task");
564
+ tarefasDetalhadas.set(task.nome, coletarResumoTask(task));
565
+ }
566
+ for (const flow of modulo.flows) {
567
+ registrar(flow.nome, "flow");
568
+ }
569
+ for (const route of modulo.routes) {
570
+ registrar(route.nome, "route");
571
+ }
572
+ for (const state of modulo.states) {
573
+ if (state.nome) {
574
+ registrar(state.nome, "state");
575
+ const transicoes = new Set((localizarBloco(state.corpo, "transitions")?.linhas ?? [])
576
+ .map((linha) => parsearTransicaoEstado(linha.conteudo))
577
+ .filter((linha) => Boolean(linha))
578
+ .map((linha) => serializarTransicao(linha.origem, linha.destino)));
579
+ statesConhecidos.set(state.nome, { transicoes });
580
+ }
581
+ }
582
+ return {
583
+ modulo: modulo.nome,
584
+ simbolos,
585
+ tiposConhecidos,
586
+ tasksConhecidas,
587
+ tarefasDetalhadas,
588
+ statesConhecidos,
589
+ modulosImportados: [],
590
+ interoperabilidades: modulo.uses
591
+ .filter(ehUseInterop)
592
+ .map((use) => ({ origem: use.origem, caminho: use.caminho })),
593
+ enumsConhecidos,
594
+ };
595
+ }
596
+ function validarTask(task, tiposConhecidos, statesConhecidos, diagnosticos) {
597
+ if (!task.input) {
598
+ diagnosticos.push(criarDiagnostico("SEM003", `Task "${task.nome}" precisa declarar input.`, "erro", task.intervalo, "Toda task precisa declarar as entradas de forma explicita."));
599
+ }
600
+ if (!task.output) {
601
+ diagnosticos.push(criarDiagnostico("SEM004", `Task "${task.nome}" precisa declarar output.`, "erro", task.intervalo, "Toda task precisa declarar a saida esperada."));
602
+ }
603
+ if (!task.guarantees) {
604
+ diagnosticos.push(criarDiagnostico("SEM005", `Task "${task.nome}" precisa declarar guarantees.`, "erro", task.intervalo, "A proposta da Sema e falhar cedo quando a pos-condicao nao esta explicita."));
605
+ }
606
+ if (task.input) {
607
+ validarCamposDeTipos(task.input.campos, tiposConhecidos, diagnosticos, `input da task ${task.nome}`);
608
+ }
609
+ if (task.output) {
610
+ validarCamposDeTipos(task.output.campos, tiposConhecidos, diagnosticos, `output da task ${task.nome}`);
611
+ }
612
+ const entradasConhecidas = new Set(task.input?.campos.map((campo) => campo.nome) ?? []);
613
+ const saidasConhecidas = new Set(task.output?.campos.map((campo) => campo.nome) ?? []);
614
+ if (task.rules) {
615
+ validarExpressoesDeclaradas(task.rules.linhas, diagnosticos, {
616
+ codigoErroSintaxe: "SEM021",
617
+ codigoErroReferencia: "SEM022",
618
+ nomeBloco: `rules da task ${task.nome}`,
619
+ simbolosPermitidos: entradasConhecidas,
620
+ dicaSintaxe: "Use expressoes como \"campo existe\", \"campo > 0\", \"campo em [A, B]\" ou \"campo deve_ser predicado\".",
621
+ dicaReferencia: "No MVP atual, rules devem referenciar apenas campos do input.",
622
+ dicaReferenciaPersonalizada: (raiz) => (saidasConhecidas.has(raiz)
623
+ ? `\"${raiz}\" parece vir do output. Rules devem validar entrada; se a intencao era afirmar pos-condicao, mova isso para guarantees.`
624
+ : undefined),
625
+ });
626
+ }
627
+ if (task.effects) {
628
+ validarEfeitosDeclarados(task.effects.linhas, diagnosticos, `effects da task ${task.nome}`);
629
+ }
630
+ validarImplementacoesTask(task, diagnosticos);
631
+ if (task.tests) {
632
+ for (const bloco of task.tests.blocos) {
633
+ if (bloco.tipo !== "caso_teste") {
634
+ continue;
635
+ }
636
+ validarCasoTeste(task, bloco, diagnosticos);
637
+ }
638
+ }
639
+ if (task.guarantees && task.output) {
640
+ validarExpressoesDeclaradas(task.guarantees.linhas, diagnosticos, {
641
+ codigoErroSintaxe: "SEM030",
642
+ codigoErroReferencia: "SEM031",
643
+ nomeBloco: `guarantees da task ${task.nome}`,
644
+ simbolosPermitidos: saidasConhecidas,
645
+ dicaSintaxe: "Use expressoes como \"saida existe\", \"saida == valor\" ou \"saida em [A, B]\" nas guarantees.",
646
+ dicaReferencia: "No MVP atual, guarantees devem referenciar campos do output ou marcadores semanticos permitidos.",
647
+ aceitarMarcadoresSemanticos: true,
648
+ dicaReferenciaPersonalizada: (raiz) => (entradasConhecidas.has(raiz)
649
+ ? `\"${raiz}\" parece vir do input. Guarantees devem afirmar output, estado ou marcadores semanticos; se a intencao era validar entrada, mova isso para rules.`
650
+ : undefined),
651
+ });
652
+ }
653
+ if (task.error) {
654
+ const nomes = new Set();
655
+ for (const campo of task.error.campos) {
656
+ if (nomes.has(campo.nome)) {
657
+ diagnosticos.push(diagnosticoDuplicado(campo.nome, "Erro", campo.intervalo));
658
+ }
659
+ nomes.add(campo.nome);
660
+ }
661
+ }
662
+ const blocoInternoTests = localizarBloco(task.corpo, "tests");
663
+ if (blocoInternoTests && blocoInternoTests.blocos.length === 0) {
664
+ diagnosticos.push(criarDiagnostico("SEM007", `Task "${task.nome}" declarou tests sem casos.`, "erro", blocoInternoTests.intervalo, "Adicione ao menos um bloco caso dentro de tests."));
665
+ }
666
+ validarVinculoEstadoDaTask(task, statesConhecidos, diagnosticos);
667
+ }
668
+ function validarCasoTeste(task, caso, diagnosticos) {
669
+ if (!caso.given) {
670
+ diagnosticos.push(criarDiagnostico("SEM008", `Caso de teste "${caso.nome}" da task "${task.nome}" precisa declarar given.`, "erro", caso.intervalo));
671
+ }
672
+ if (!caso.expect) {
673
+ diagnosticos.push(criarDiagnostico("SEM009", `Caso de teste "${caso.nome}" da task "${task.nome}" precisa declarar expect.`, "erro", caso.intervalo));
674
+ }
675
+ }
676
+ export function analisarSemantica(modulo, opcoes = {}) {
677
+ const diagnosticos = [];
678
+ const simbolos = new Map();
679
+ const tiposConhecidos = new Set(TIPOS_PRIMITIVOS);
680
+ const tasksConhecidas = new Set();
681
+ const tarefasDetalhadas = new Map();
682
+ const statesConhecidos = new Map();
683
+ const modulosImportados = [];
684
+ const interoperabilidades = [];
685
+ const enumsConhecidos = new Map();
686
+ for (const use of modulo.uses) {
687
+ if (use.origem !== "sema") {
688
+ if (!PADRAO_CAMINHO_INTEROP.test(use.caminho)) {
689
+ diagnosticos.push(criarDiagnostico("SEM058", `Interop externa "${use.origem} ${use.caminho}" e invalida no modulo "${modulo.nome}".`, "erro", use.intervalo, "Use um identificador de modulo externo como pacote.servico, app.modulo ou dominio.executor."));
690
+ continue;
691
+ }
692
+ interoperabilidades.push({ origem: use.origem, caminho: use.caminho });
693
+ continue;
694
+ }
695
+ const resolucaoUse = resolverUseSema(modulo.nome, use.caminho, opcoes.contextosModulos);
696
+ const contextoImportado = resolucaoUse.caminhoResolvido
697
+ ? opcoes.contextosModulos?.get(resolucaoUse.caminhoResolvido)
698
+ : undefined;
699
+ if (!contextoImportado) {
700
+ const candidatosEncontrados = resolucaoUse.candidatosRelativos
701
+ .filter((candidato) => opcoes.contextosModulos?.has(candidato))
702
+ .slice(0, 4);
703
+ const sugestoesModulos = descreverSugestoes(opcoes.contextosModulos?.keys() ?? [], "Modulos disponiveis neste contexto");
704
+ diagnosticos.push(criarDiagnostico("SEM019", `Modulo "${modulo.nome}" usa "${use.caminho}", mas esse modulo nao foi encontrado no projeto atual.`, "erro", use.intervalo, candidatosEncontrados.length > 0
705
+ ? `Se a intencao era um import relativo ao namespace atual, tente um caminho como ${candidatosEncontrados.join(", ")}.`
706
+ : (sugestoesModulos ?? "Garanta que o arquivo .sema importado esteja presente no mesmo conjunto de compilacao.")));
707
+ continue;
708
+ }
709
+ modulosImportados.push(resolucaoUse.caminhoResolvido ?? use.caminho);
710
+ for (const tipo of contextoImportado.tiposConhecidos) {
711
+ tiposConhecidos.add(tipo);
712
+ }
713
+ for (const task of contextoImportado.tasksConhecidas) {
714
+ tasksConhecidas.add(task);
715
+ }
716
+ for (const [nomeTask, detalhesTask] of contextoImportado.tarefasDetalhadas) {
717
+ tarefasDetalhadas.set(nomeTask, {
718
+ input: detalhesTask.input.map((campo) => ({ ...campo, modificadores: [...campo.modificadores] })),
719
+ output: detalhesTask.output.map((campo) => ({ ...campo, modificadores: [...campo.modificadores] })),
720
+ errors: detalhesTask.errors.map((erro) => ({ ...erro })),
721
+ guarantees: [...detalhesTask.guarantees],
722
+ implementacoes: detalhesTask.implementacoes.map((impl) => ({ ...impl })),
723
+ });
724
+ }
725
+ for (const [nomeState, metadadosState] of contextoImportado.statesConhecidos) {
726
+ statesConhecidos.set(nomeState, { transicoes: new Set(metadadosState.transicoes) });
727
+ }
728
+ for (const [nomeEnum, valores] of contextoImportado.enumsConhecidos) {
729
+ enumsConhecidos.set(nomeEnum, new Set(valores));
730
+ }
731
+ for (const interop of contextoImportado.interoperabilidades) {
732
+ interoperabilidades.push({ ...interop });
733
+ }
734
+ }
735
+ const registrar = (nome, categoria, intervalo) => {
736
+ if (simbolos.has(nome)) {
737
+ diagnosticos.push(diagnosticoDuplicado(nome, categoria, intervalo));
738
+ return;
739
+ }
740
+ simbolos.set(nome, { nome, categoria });
741
+ if (categoria === "task") {
742
+ tasksConhecidas.add(nome);
743
+ return;
744
+ }
745
+ tiposConhecidos.add(nome);
746
+ };
747
+ for (const type of modulo.types) {
748
+ registrar(type.nome, "tipo", type.intervalo);
749
+ }
750
+ for (const entity of modulo.entities) {
751
+ registrar(entity.nome, "entity", entity.intervalo);
752
+ }
753
+ for (const enumeracao of modulo.enums) {
754
+ registrar(enumeracao.nome, "enum", enumeracao.intervalo);
755
+ enumsConhecidos.set(enumeracao.nome, new Set(enumeracao.valores));
756
+ }
757
+ for (const task of modulo.tasks) {
758
+ registrar(task.nome, "task", task.intervalo);
759
+ tarefasDetalhadas.set(task.nome, coletarResumoTask(task));
760
+ }
761
+ for (const flow of modulo.flows) {
762
+ registrar(flow.nome, "flow", flow.intervalo);
763
+ }
764
+ for (const route of modulo.routes) {
765
+ registrar(route.nome, "route", route.intervalo);
766
+ }
767
+ for (const state of modulo.states) {
768
+ if (state.nome) {
769
+ registrar(state.nome, "state", state.intervalo);
770
+ const transicoes = new Set((localizarBloco(state.corpo, "transitions")?.linhas ?? [])
771
+ .map((linha) => parsearTransicaoEstado(linha.conteudo))
772
+ .filter((linha) => Boolean(linha))
773
+ .map((linha) => serializarTransicao(linha.origem, linha.destino)));
774
+ statesConhecidos.set(state.nome, { transicoes });
775
+ }
776
+ }
777
+ for (const type of modulo.types) {
778
+ validarCamposDeTipos(type.corpo.campos, tiposConhecidos, diagnosticos, `type ${type.nome}`);
779
+ const fields = localizarBloco(type.corpo, "fields");
780
+ if (fields) {
781
+ validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `fields do type ${type.nome}`);
782
+ }
783
+ validarInvariantesDeCampos(type.corpo, `type ${type.nome}`, diagnosticos);
784
+ }
785
+ for (const entity of modulo.entities) {
786
+ const fields = localizarBloco(entity.corpo, "fields");
787
+ if (!fields || fields.campos.length === 0) {
788
+ diagnosticos.push(criarDiagnostico("SEM010", `Entity "${entity.nome}" precisa declarar fields.`, "erro", entity.intervalo, "Adicione um bloco fields com os campos da entidade."));
789
+ }
790
+ else {
791
+ validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `entity ${entity.nome}`);
792
+ }
793
+ validarInvariantesDeCampos(entity.corpo, `entity ${entity.nome}`, diagnosticos);
794
+ }
795
+ for (const task of modulo.tasks) {
796
+ validarTask(task, tiposConhecidos, statesConhecidos, diagnosticos);
797
+ }
798
+ for (const flow of modulo.flows) {
799
+ validarFlow(flow, tasksConhecidas, tarefasDetalhadas, diagnosticos);
800
+ }
801
+ for (const route of modulo.routes) {
802
+ validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos);
803
+ }
804
+ const assinaturasRoute = new Map();
805
+ for (const route of modulo.routes) {
806
+ const metodo = (localizarCampo(route.corpo, "metodo")?.valor ?? "").toUpperCase();
807
+ const caminho = recomporCaminhoRoute(localizarCampo(route.corpo, "caminho")) ?? "";
808
+ if (!metodo || !caminho) {
809
+ continue;
810
+ }
811
+ const chave = `${metodo} ${caminho}`;
812
+ const existente = assinaturasRoute.get(chave);
813
+ if (existente) {
814
+ diagnosticos.push(criarDiagnostico("SEM055", `Route "${route.nome}" reutiliza a assinatura publica "${chave}", ja declarada por "${existente.nome}".`, "erro", route.intervalo, "Cada combinacao de metodo e caminho deve ser unica no mesmo modulo."));
815
+ continue;
816
+ }
817
+ assinaturasRoute.set(chave, route);
818
+ }
819
+ for (const state of modulo.states) {
820
+ validarState(state, tiposConhecidos, enumsConhecidos, diagnosticos);
821
+ }
822
+ return {
823
+ contexto: {
824
+ modulo: modulo.nome,
825
+ simbolos,
826
+ tiposConhecidos,
827
+ tasksConhecidas,
828
+ tarefasDetalhadas,
829
+ statesConhecidos,
830
+ modulosImportados,
831
+ interoperabilidades,
832
+ enumsConhecidos,
833
+ },
834
+ diagnosticos,
835
+ };
836
+ }
837
+ //# sourceMappingURL=analisador.js.map