@semacode/cli 1.5.14 → 1.5.16

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 (177) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +76 -43
  3. package/package.json +26 -35
  4. package/semacode-cli-1.3.1.tgz +0 -0
  5. package/src/angular-consumer-standalone.ts +312 -0
  6. package/src/cpp-symbols.ts +82 -0
  7. package/src/docs.ts +535 -0
  8. package/src/dotnet-http.ts +355 -0
  9. package/src/drift.ts +4933 -0
  10. package/src/go-http.ts +118 -0
  11. package/src/importador.ts +3891 -0
  12. package/src/index.ts +5641 -0
  13. package/src/java-http.ts +247 -0
  14. package/src/lua-symbols.ts +114 -0
  15. package/src/php-symbols.ts +462 -0
  16. package/src/projeto.ts +862 -0
  17. package/src/python-http.ts +258 -0
  18. package/src/rust-http.ts +125 -0
  19. package/src/tipos.ts +24 -0
  20. package/src/typescript-http.ts +1076 -0
  21. package/tsconfig.json +20 -0
  22. package/AGENTS.md +0 -272
  23. package/LICENSE +0 -22
  24. package/SEMA_BRIEF.curto.txt +0 -9
  25. package/SEMA_BRIEF.md +0 -63
  26. package/SEMA_BRIEF.micro.txt +0 -7
  27. package/SEMA_INDEX.json +0 -799
  28. package/dist/angular-consumer-standalone.d.ts +0 -6
  29. package/dist/angular-consumer-standalone.js.map +0 -1
  30. package/dist/cpp-symbols.d.ts +0 -10
  31. package/dist/cpp-symbols.js.map +0 -1
  32. package/dist/docs.d.ts +0 -56
  33. package/dist/docs.js.map +0 -1
  34. package/dist/dotnet-http.d.ts +0 -23
  35. package/dist/dotnet-http.js.map +0 -1
  36. package/dist/drift.d.ts +0 -225
  37. package/dist/drift.js.map +0 -1
  38. package/dist/go-http.d.ts +0 -23
  39. package/dist/go-http.js.map +0 -1
  40. package/dist/importador.d.ts +0 -31
  41. package/dist/importador.js.map +0 -1
  42. package/dist/index.d.ts +0 -2
  43. package/dist/index.js.map +0 -1
  44. package/dist/java-http.d.ts +0 -23
  45. package/dist/java-http.js.map +0 -1
  46. package/dist/lua-symbols.d.ts +0 -10
  47. package/dist/lua-symbols.js.map +0 -1
  48. package/dist/php-symbols.d.ts +0 -24
  49. package/dist/php-symbols.js.map +0 -1
  50. package/dist/projeto.d.ts +0 -53
  51. package/dist/projeto.js.map +0 -1
  52. package/dist/python-http.d.ts +0 -23
  53. package/dist/python-http.js.map +0 -1
  54. package/dist/rust-http.d.ts +0 -23
  55. package/dist/rust-http.js.map +0 -1
  56. package/dist/tipos.d.ts +0 -3
  57. package/dist/tipos.js.map +0 -1
  58. package/dist/typescript-http.d.ts +0 -35
  59. package/dist/typescript-http.js.map +0 -1
  60. package/docs/AGENT_STARTER.md +0 -102
  61. package/docs/cli.md +0 -117
  62. package/docs/como-ensinar-a-sema-para-ia.md +0 -149
  63. package/docs/deploy.md +0 -70
  64. package/docs/documentacao.md +0 -63
  65. package/docs/env.md +0 -56
  66. package/docs/extensao-vscode.md +0 -45
  67. package/docs/fluxo-pratico-ia-sema.md +0 -177
  68. package/docs/instalacao-e-primeiro-uso.md +0 -112
  69. package/docs/integracao-com-ia.md +0 -101
  70. package/docs/mcp.md +0 -53
  71. package/docs/pagamento-ponta-a-ponta.md +0 -155
  72. package/docs/persistencia-vendor-first.md +0 -145
  73. package/docs/prompt-base-ia-sema.md +0 -104
  74. package/docs/rollback.md +0 -47
  75. package/docs/sintaxe.md +0 -410
  76. package/exemplos/agendamento.sema +0 -106
  77. package/exemplos/assinatura.sema +0 -136
  78. package/exemplos/auditoria.sema +0 -88
  79. package/exemplos/autenticacao.sema +0 -125
  80. package/exemplos/automacao.sema +0 -107
  81. package/exemplos/cadastro_usuario.sema +0 -54
  82. package/exemplos/calculadora.sema +0 -78
  83. package/exemplos/crud_simples.sema +0 -89
  84. package/exemplos/estoque.sema +0 -126
  85. package/exemplos/exportacao.sema +0 -94
  86. package/exemplos/fila.sema +0 -131
  87. package/exemplos/integracao_externa.sema +0 -94
  88. package/exemplos/multi_tenant.sema +0 -140
  89. package/exemplos/notificacao.sema +0 -98
  90. package/exemplos/operacao_estrategia.sema +0 -402
  91. package/exemplos/pagamento.sema +0 -222
  92. package/exemplos/pagamento_dominio.sema +0 -35
  93. package/exemplos/pedido.sema +0 -119
  94. package/exemplos/permissao.sema +0 -121
  95. package/exemplos/persistencia_vendor_first.sema +0 -86
  96. package/exemplos/relatorio.sema +0 -93
  97. package/exemplos/testes_embutidos.sema +0 -45
  98. package/exemplos/tratamento_erro.sema +0 -157
  99. package/exemplos/upload_arquivo.sema +0 -93
  100. package/exemplos/webhook.sema +0 -96
  101. package/llms-full.txt +0 -34
  102. package/llms.txt +0 -17
  103. package/node_modules/@sema/gerador-css/dist/index.d.ts +0 -3
  104. package/node_modules/@sema/gerador-css/dist/index.js +0 -592
  105. package/node_modules/@sema/gerador-css/dist/index.js.map +0 -1
  106. package/node_modules/@sema/gerador-css/package.json +0 -7
  107. package/node_modules/@sema/gerador-dart/dist/index.d.ts +0 -3
  108. package/node_modules/@sema/gerador-dart/dist/index.js +0 -44
  109. package/node_modules/@sema/gerador-dart/dist/index.js.map +0 -1
  110. package/node_modules/@sema/gerador-dart/package.json +0 -7
  111. package/node_modules/@sema/gerador-html/dist/index.d.ts +0 -3
  112. package/node_modules/@sema/gerador-html/dist/index.js +0 -163
  113. package/node_modules/@sema/gerador-html/dist/index.js.map +0 -1
  114. package/node_modules/@sema/gerador-html/package.json +0 -7
  115. package/node_modules/@sema/gerador-javascript/dist/index.d.ts +0 -3
  116. package/node_modules/@sema/gerador-javascript/dist/index.js +0 -421
  117. package/node_modules/@sema/gerador-javascript/dist/index.js.map +0 -1
  118. package/node_modules/@sema/gerador-javascript/package.json +0 -7
  119. package/node_modules/@sema/gerador-lua/dist/index.d.ts +0 -3
  120. package/node_modules/@sema/gerador-lua/dist/index.js +0 -328
  121. package/node_modules/@sema/gerador-lua/dist/index.js.map +0 -1
  122. package/node_modules/@sema/gerador-lua/package.json +0 -7
  123. package/node_modules/@sema/gerador-python/dist/index.d.ts +0 -6
  124. package/node_modules/@sema/gerador-python/dist/index.js +0 -729
  125. package/node_modules/@sema/gerador-python/dist/index.js.map +0 -1
  126. package/node_modules/@sema/gerador-python/package.json +0 -7
  127. package/node_modules/@sema/gerador-typescript/dist/index.d.ts +0 -6
  128. package/node_modules/@sema/gerador-typescript/dist/index.js +0 -793
  129. package/node_modules/@sema/gerador-typescript/dist/index.js.map +0 -1
  130. package/node_modules/@sema/gerador-typescript/package.json +0 -7
  131. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +0 -125
  132. package/node_modules/@sema/nucleo/dist/ast/tipos.js +0 -2
  133. package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +0 -1
  134. package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +0 -21
  135. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +0 -12
  136. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +0 -1
  137. package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +0 -9
  138. package/node_modules/@sema/nucleo/dist/formatador/index.js +0 -487
  139. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +0 -1
  140. package/node_modules/@sema/nucleo/dist/index.d.ts +0 -35
  141. package/node_modules/@sema/nucleo/dist/index.js +0 -96
  142. package/node_modules/@sema/nucleo/dist/index.js.map +0 -1
  143. package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +0 -5
  144. package/node_modules/@sema/nucleo/dist/ir/conversor.js +0 -1058
  145. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
  146. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +0 -377
  147. package/node_modules/@sema/nucleo/dist/ir/modelos.js +0 -2
  148. package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +0 -1
  149. package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +0 -7
  150. package/node_modules/@sema/nucleo/dist/lexer/lexer.js +0 -122
  151. package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +0 -1
  152. package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +0 -8
  153. package/node_modules/@sema/nucleo/dist/lexer/tokens.js +0 -82
  154. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +0 -1
  155. package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +0 -9
  156. package/node_modules/@sema/nucleo/dist/parser/parser.js +0 -807
  157. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +0 -1
  158. package/node_modules/@sema/nucleo/dist/persistencia/contratos.d.ts +0 -39
  159. package/node_modules/@sema/nucleo/dist/persistencia/contratos.js +0 -294
  160. package/node_modules/@sema/nucleo/dist/persistencia/contratos.js.map +0 -1
  161. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +0 -58
  162. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +0 -1912
  163. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +0 -1
  164. package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +0 -104
  165. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +0 -445
  166. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +0 -1
  167. package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +0 -91
  168. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +0 -258
  169. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +0 -1
  170. package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +0 -2
  171. package/node_modules/@sema/nucleo/dist/util/arquivos.js +0 -25
  172. package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +0 -1
  173. package/node_modules/@sema/nucleo/package.json +0 -7
  174. package/node_modules/@sema/padroes/dist/index.d.ts +0 -25
  175. package/node_modules/@sema/padroes/dist/index.js +0 -316
  176. package/node_modules/@sema/padroes/dist/index.js.map +0 -1
  177. package/node_modules/@sema/padroes/package.json +0 -7
@@ -1,1912 +0,0 @@
1
- import { criarDiagnostico } from "../diagnosticos/index.js";
2
- import { CAMPOS_DATABASE_SUPORTADOS, CAMPOS_RECURSO_PERSISTENCIA_SUPORTADOS, classificarCompatibilidadePersistencia, nomeTipoRecursoPersistencia, normalizarConsistenciaPersistencia, normalizarDurabilidadePersistencia, normalizarEngineBanco, normalizarModeloConsultaPersistencia, normalizarModeloTransacaoPersistencia, parsearBooleanoPersistencia, recursoPersistenciaPodeSerPortavel, } from "../persistencia/contratos.js";
3
- import { ehCategoriaEfeitoSemantico, ehCriticidadeEfeitoSemantico, extrairReferenciasDaExpressao, parsearEfeitoSemantico, parsearEtapaFlow, parsearExpressaoSemantica, parsearTransicaoEstado, } from "./estruturas.js";
4
- import { CLASSIFICACOES_DADO_SUPORTADAS, MODOS_AUTH_SUPORTADOS, MOTIVOS_AUDIT_SUPORTADOS, ORIGENS_AUTH_SUPORTADAS, PRINCIPAIS_AUTH_SUPORTADOS, REDACOES_LOG_SUPORTADAS, TENANTS_AUTHZ_SUPORTADOS, contratoDadosTemSegredoOuCredencial, contratoDadosTemSensivel, extrairContratoAudit, extrairContratoAuth, extrairContratoAuthz, extrairContratoDados, extrairContratoForbidden, extrairContratoSegredos, efeitoEhPrivilegiado, efeitoRequerSegredo, forbiddenContemRegra, } from "./seguranca.js";
5
- function ehUseInterop(use) {
6
- return use.origem !== "sema";
7
- }
8
- const TIPOS_PRIMITIVOS = new Set([
9
- "Texto",
10
- "Numero",
11
- "Inteiro",
12
- "Decimal",
13
- "Booleano",
14
- "Data",
15
- "DataHora",
16
- "Id",
17
- "Email",
18
- "Url",
19
- "Json",
20
- "Vazio",
21
- ]);
22
- const TIPOS_COMPOSTOS_SUPORTADOS = new Set(["Lista", "Mapa", "Opcional", "Ou"]);
23
- const CAMPOS_VINCULO_SUPORTADOS = new Set([
24
- "arquivo",
25
- "simbolo",
26
- "recurso",
27
- "superficie",
28
- "rota",
29
- "teste",
30
- "tabela",
31
- "fila",
32
- "job",
33
- "policy",
34
- "artefato",
35
- "evento",
36
- "cache",
37
- "storage",
38
- "worker",
39
- "cron",
40
- "webhook",
41
- ]);
42
- const CAMPOS_EXECUCAO_SUPORTADOS = new Set([
43
- "idempotencia",
44
- "timeout",
45
- "retry",
46
- "compensacao",
47
- "criticidade_operacional",
48
- ]);
49
- const CAMPOS_AUTH_SUPORTADOS = new Set([
50
- "modo",
51
- "estrategia",
52
- "principal",
53
- "origem",
54
- ]);
55
- const CAMPOS_AUTHZ_SUPORTADOS = new Set([
56
- "papel",
57
- "papeis",
58
- "escopo",
59
- "escopos",
60
- "politica",
61
- "tenant",
62
- ]);
63
- const CAMPOS_DADOS_SUPORTADOS = new Set([
64
- "classificacao_padrao",
65
- "redacao_log",
66
- "retencao",
67
- ]);
68
- const CAMPOS_AUDIT_SUPORTADOS = new Set([
69
- "evento",
70
- "ator",
71
- "correlacao",
72
- "retencao",
73
- "motivo",
74
- ]);
75
- const CAMPOS_SEGREDO_SUPORTADOS = new Set([
76
- "origem",
77
- "escopo",
78
- "acesso",
79
- "rotacao",
80
- "nao_logar",
81
- "nao_retornar",
82
- "mascarar",
83
- ]);
84
- const CAMPOS_ERRO_OPERACIONAL = new Set([
85
- "mensagem",
86
- "categoria",
87
- "recuperabilidade",
88
- "acao_chamador",
89
- "impacta_estado",
90
- "requer_compensacao",
91
- ]);
92
- const CRITICIDADES_OPERACIONAIS = new Set(["baixa", "media", "alta", "critica"]);
93
- const TIPOS_AUTHOR_PROFILE = new Set([
94
- "book",
95
- "work",
96
- "part",
97
- "chapter",
98
- "section",
99
- "scene",
100
- "character",
101
- "arc",
102
- "audience",
103
- "thesis",
104
- "argument",
105
- "claim",
106
- "source",
107
- "concept",
108
- "example",
109
- "voice",
110
- "style_rule",
111
- "lexicon",
112
- "motif",
113
- "canon",
114
- ]);
115
- const TERMOS_AUTHOR_SENSIVEIS = [
116
- "autismo",
117
- "autista",
118
- "autistas",
119
- "tea",
120
- "neurodiversidade",
121
- "neurodivergencia",
122
- "neurodivergente",
123
- "saude",
124
- "clinico",
125
- "clinica",
126
- "medico",
127
- "medica",
128
- "diagnostico",
129
- "diagnosticar",
130
- "terapia",
131
- "tratamento",
132
- "medicamento",
133
- "psicologia",
134
- "psiquiatria",
135
- "educacao inclusiva",
136
- "inclusao escolar",
137
- "juridico",
138
- "legal",
139
- "financeiro",
140
- ];
141
- const PADRAO_CAMINHO_INTEROP = /^[A-Za-z_][A-Za-z0-9_-]*(\.[A-Za-z_][A-Za-z0-9_-]*)*$/;
142
- function normalizarOrigemImplementacao(valor) {
143
- switch (valor.toLowerCase()) {
144
- case "ts":
145
- case "typescript":
146
- return "ts";
147
- case "py":
148
- case "python":
149
- return "py";
150
- case "dart":
151
- return "dart";
152
- case "lua":
153
- return "lua";
154
- case "php":
155
- return "php";
156
- case "cs":
157
- case "csharp":
158
- case "dotnet":
159
- return "cs";
160
- case "java":
161
- return "java";
162
- case "go":
163
- case "golang":
164
- return "go";
165
- case "rust":
166
- case "rs":
167
- return "rust";
168
- case "cpp":
169
- case "cxx":
170
- case "cc":
171
- case "c++":
172
- return "cpp";
173
- default:
174
- return undefined;
175
- }
176
- }
177
- function extrairReferenciasDeTipos(texto) {
178
- const correspondencias = texto.match(/[A-Z][A-Za-z0-9_]*/g);
179
- return (correspondencias ?? []).filter((referencia) => !TIPOS_COMPOSTOS_SUPORTADOS.has(referencia));
180
- }
181
- function extrairRaiz(referencia) {
182
- return referencia.split(".")[0] ?? referencia;
183
- }
184
- function ehMarcadorSemantico(referencia) {
185
- return ["persistencia", "sucesso", "estado"].includes(extrairRaiz(referencia));
186
- }
187
- function diagnosticoDuplicado(nome, categoria, intervalo) {
188
- return criarDiagnostico("SEM001", `${categoria} "${nome}" foi declarado mais de uma vez no mesmo modulo.`, "erro", intervalo, "Use nomes unicos para simbolos do modulo.");
189
- }
190
- function validarCamposDeTipos(campos, tiposConhecidos, diagnosticos, contexto) {
191
- for (const campo of campos) {
192
- const referencias = extrairReferenciasDeTipos(campo.valor);
193
- for (const referencia of referencias) {
194
- if (!tiposConhecidos.has(referencia)) {
195
- diagnosticos.push(criarDiagnostico("SEM002", `Tipo "${referencia}" nao foi encontrado em ${contexto}.`, "erro", campo.intervalo, "Declare o tipo, entidade ou enum antes de usa-lo."));
196
- }
197
- }
198
- }
199
- }
200
- function localizarBloco(corpo, nome) {
201
- if (!corpo) {
202
- return undefined;
203
- }
204
- return corpo.blocos.find((bloco) => bloco.tipo === "bloco_generico" && (bloco.palavraChave === nome || bloco.nome === nome));
205
- }
206
- function nomeBlocoDeclarativo(bloco) {
207
- return bloco.palavraChave === "desconhecido"
208
- ? (bloco.nome ?? "desconhecido")
209
- : bloco.palavraChave;
210
- }
211
- function blocoTemNome(bloco, nomes) {
212
- return nomes.includes(nomeBlocoDeclarativo(bloco).toLowerCase());
213
- }
214
- function localizarBlocoPorNomes(corpo, ...nomes) {
215
- return corpo?.blocos.find((bloco) => bloco.tipo === "bloco_generico" && blocoTemNome(bloco, nomes));
216
- }
217
- function localizarCampo(bloco, ...nomes) {
218
- if (!bloco) {
219
- return undefined;
220
- }
221
- return bloco.campos.find((campo) => nomes.includes(campo.nome));
222
- }
223
- function valorCampoCompleto(campo) {
224
- if (!campo) {
225
- return undefined;
226
- }
227
- return [campo.valor, ...campo.modificadores].join(" ").trim() || undefined;
228
- }
229
- function parsearBooleanoSemantico(valor) {
230
- if (!valor) {
231
- return undefined;
232
- }
233
- if (valor === "verdadeiro" || valor === "true") {
234
- return true;
235
- }
236
- if (valor === "falso" || valor === "false") {
237
- return false;
238
- }
239
- return undefined;
240
- }
241
- function converterCampoSemantico(campo) {
242
- return {
243
- nome: campo.nome,
244
- tipo: campo.valor,
245
- modificadores: [...campo.modificadores],
246
- };
247
- }
248
- function indicesCampos(campos) {
249
- return new Map(campos.map((campo) => [campo.nome, campo]));
250
- }
251
- function indiceErros(erros) {
252
- return new Map(erros.map((erro) => [erro.codigo, erro]));
253
- }
254
- function coletarErrosTask(task) {
255
- const erros = new Map();
256
- for (const campo of task.error?.campos ?? []) {
257
- erros.set(campo.nome, {
258
- codigo: campo.nome,
259
- mensagem: [campo.valor, ...campo.modificadores].join(" ").trim(),
260
- });
261
- }
262
- for (const bloco of task.error?.blocos ?? []) {
263
- if (bloco.tipo !== "bloco_generico") {
264
- continue;
265
- }
266
- const codigo = bloco.nome ?? bloco.palavraChave;
267
- if (!codigo || codigo === "desconhecido") {
268
- continue;
269
- }
270
- erros.set(codigo, {
271
- codigo,
272
- mensagem: valorCampoCompleto(localizarCampo(bloco, "mensagem")) ?? `Erro estruturado "${codigo}".`,
273
- categoria: valorCampoCompleto(localizarCampo(bloco, "categoria")),
274
- recuperabilidade: valorCampoCompleto(localizarCampo(bloco, "recuperabilidade")),
275
- acaoChamador: valorCampoCompleto(localizarCampo(bloco, "acao_chamador")),
276
- impactaEstado: parsearBooleanoSemantico(valorCampoCompleto(localizarCampo(bloco, "impacta_estado"))),
277
- requerCompensacao: parsearBooleanoSemantico(valorCampoCompleto(localizarCampo(bloco, "requer_compensacao"))),
278
- });
279
- }
280
- for (const bloco of task.tests?.blocos ?? []) {
281
- if (bloco.tipo !== "caso_teste") {
282
- continue;
283
- }
284
- const codigoErro = bloco.error?.campos.find((campo) => campo.nome === "tipo")?.valor;
285
- if (codigoErro && !erros.has(codigoErro)) {
286
- erros.set(codigoErro, {
287
- codigo: codigoErro,
288
- mensagem: `Erro sintetico derivado do caso de teste "${bloco.nome}".`,
289
- });
290
- }
291
- }
292
- return [...erros.values()];
293
- }
294
- function coletarResumoTask(task) {
295
- return {
296
- input: (task.input?.campos ?? []).map(converterCampoSemantico),
297
- output: (task.output?.campos ?? []).map(converterCampoSemantico),
298
- errors: coletarErrosTask(task),
299
- guarantees: (task.guarantees?.linhas ?? []).map((linha) => linha.conteudo),
300
- implementacoes: (task.impl?.campos ?? [])
301
- .map((campo) => {
302
- const origem = normalizarOrigemImplementacao(campo.nome);
303
- return origem ? { origem, caminho: campo.valor } : undefined;
304
- })
305
- .filter((item) => Boolean(item)),
306
- };
307
- }
308
- function validarImplementacoesTask(task, diagnosticos) {
309
- if (!task.impl) {
310
- return;
311
- }
312
- const origens = new Set();
313
- for (const campo of task.impl.campos) {
314
- const origem = normalizarOrigemImplementacao(campo.nome);
315
- if (!origem) {
316
- diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use apenas ts, py, dart, lua, php, cs, java, go, rust ou cpp dentro do bloco impl."));
317
- continue;
318
- }
319
- if (origens.has(origem)) {
320
- 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."));
321
- continue;
322
- }
323
- origens.add(origem);
324
- if (!PADRAO_CAMINHO_INTEROP.test(campo.valor)) {
325
- 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."));
326
- }
327
- }
328
- }
329
- function validarVinculos(bloco, diagnosticos, contexto) {
330
- if (!bloco) {
331
- return;
332
- }
333
- for (const campo of bloco.campos) {
334
- if (!CAMPOS_VINCULO_SUPORTADOS.has(campo.nome)) {
335
- diagnosticos.push(criarDiagnostico("SEM064", `Campo de vinculo "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use arquivo, simbolo, recurso, superficie, rota, teste, tabela, fila, job, policy, artefato, evento, cache, storage, worker, cron ou webhook."));
336
- }
337
- }
338
- }
339
- function extrairPerfilCompatibilidade(bloco, padrao = "interno") {
340
- const perfil = bloco
341
- ? valorCampoCompleto(localizarCampo(bloco, "perfil", "compatibilidade"))?.toLowerCase()
342
- : undefined;
343
- if (perfil === "publico"
344
- || perfil === "interno"
345
- || perfil === "experimental"
346
- || perfil === "legado"
347
- || perfil === "deprecado") {
348
- return perfil;
349
- }
350
- return padrao;
351
- }
352
- function validarCamposSuportadosPersistencia(bloco, camposSuportados, diagnosticos, contexto, codigo, dica) {
353
- for (const campo of bloco.campos) {
354
- if (camposSuportados.has(campo.nome)) {
355
- continue;
356
- }
357
- diagnosticos.push(criarDiagnostico(codigo, `Campo de persistencia "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, dica));
358
- }
359
- }
360
- function validarBooleanoPersistencia(valor, intervalo, diagnosticos, codigo, mensagem) {
361
- if (!valor) {
362
- return;
363
- }
364
- if (parsearBooleanoPersistencia(valor) !== undefined) {
365
- return;
366
- }
367
- diagnosticos.push(criarDiagnostico(codigo, mensagem, "erro", intervalo, "Use verdadeiro/falso ou true/false."));
368
- }
369
- function validarRecursoPersistencia(database, recurso, diagnosticos) {
370
- const engine = normalizarEngineBanco(valorCampoCompleto(localizarCampo(database, "engine")));
371
- if (!engine) {
372
- return;
373
- }
374
- validarCamposSuportadosPersistencia(recurso, CAMPOS_RECURSO_PERSISTENCIA_SUPORTADOS, diagnosticos, `resource "${recurso.nome ?? recurso.palavraChave}" do database "${database.nome ?? "database"}"`, "SEM106", "Use apenas entity, consistency, durability, transaction_model, query_model, portavel, mode, isolation, strategy, ttl, retention, path, from, to, surface, adapter, resource_kind, collection, table ou compatibilidade.");
375
- const tipoRecurso = nomeTipoRecursoPersistencia(recurso);
376
- if (!tipoRecurso) {
377
- return;
378
- }
379
- const nomeRecurso = recurso.nome ?? tipoRecurso;
380
- const contexto = `resource "${nomeRecurso}" do database "${database.nome ?? "database"}"`;
381
- const consistency = valorCampoCompleto(localizarCampo(recurso, "consistency"));
382
- const durability = valorCampoCompleto(localizarCampo(recurso, "durability"));
383
- const transactionModel = valorCampoCompleto(localizarCampo(recurso, "transaction_model"));
384
- const queryModel = valorCampoCompleto(localizarCampo(recurso, "query_model"));
385
- const mode = valorCampoCompleto(localizarCampo(recurso, "mode"));
386
- const portavel = valorCampoCompleto(localizarCampo(recurso, "portavel"));
387
- const isolation = valorCampoCompleto(localizarCampo(recurso, "isolation"));
388
- if (consistency && !normalizarConsistenciaPersistencia(consistency)) {
389
- diagnosticos.push(criarDiagnostico("SEM107", `${contexto} declarou consistency invalida: "${consistency}".`, "erro", localizarCampo(recurso, "consistency")?.intervalo, "Use eventual, forte, serializable, snapshot ou causal."));
390
- }
391
- if (durability && !normalizarDurabilidadePersistencia(durability)) {
392
- diagnosticos.push(criarDiagnostico("SEM108", `${contexto} declarou durability invalida: "${durability}".`, "erro", localizarCampo(recurso, "durability")?.intervalo, "Use baixa, media ou alta."));
393
- }
394
- if (transactionModel && !normalizarModeloTransacaoPersistencia(transactionModel)) {
395
- diagnosticos.push(criarDiagnostico("SEM109", `${contexto} declarou transaction_model invalido: "${transactionModel}".`, "erro", localizarCampo(recurso, "transaction_model")?.intervalo, "Use mvcc, bloqueio, documento, otimista ou single_thread."));
396
- }
397
- if (queryModel && !normalizarModeloConsultaPersistencia(queryModel)) {
398
- diagnosticos.push(criarDiagnostico("SEM110", `${contexto} declarou query_model invalido: "${queryModel}".`, "erro", localizarCampo(recurso, "query_model")?.intervalo, "Use sql, documento, chave_valor, pipeline ou stream."));
399
- }
400
- if (mode && !normalizarModeloConsultaPersistencia(mode)) {
401
- diagnosticos.push(criarDiagnostico("SEM111", `${contexto} declarou mode invalido: "${mode}".`, "erro", localizarCampo(recurso, "mode")?.intervalo, "Use sql, documento, chave_valor, pipeline ou stream."));
402
- }
403
- const compatibilidade = classificarCompatibilidadePersistencia(tipoRecurso, engine, { mode, isolation });
404
- if (compatibilidade.status === "invalido") {
405
- diagnosticos.push(criarDiagnostico("SEM112", `${contexto} nao e compativel com o engine ${engine}.`, "erro", recurso.intervalo, compatibilidade.motivo));
406
- }
407
- validarBooleanoPersistencia(portavel, localizarCampo(recurso, "portavel")?.intervalo ?? recurso.intervalo, diagnosticos, "SEM113", `${contexto} declarou portavel com valor invalido: "${portavel}".`);
408
- if (parsearBooleanoPersistencia(portavel) && !recursoPersistenciaPodeSerPortavel(tipoRecurso, { mode, isolation })) {
409
- diagnosticos.push(criarDiagnostico("SEM114", `${contexto} foi marcado como portavel, mas a compatibilidade entre os cinco bancos nao fecha sem perdas reais.`, "aviso", localizarCampo(recurso, "portavel")?.intervalo ?? recurso.intervalo, "Remova portavel ou reduza o recurso para um baseline comum entre postgres, mysql, sqlite, mongodb e redis."));
410
- }
411
- }
412
- function validarDatabase(database, diagnosticos) {
413
- validarCamposSuportadosPersistencia(database, CAMPOS_DATABASE_SUPORTADOS, diagnosticos, `database "${database.nome ?? "database"}"`, "SEM100", "Use apenas engine, schema, database, consistency, durability, transaction_model, query_model, portavel, adapter, perfil ou compatibilidade.");
414
- const campoEngine = localizarCampo(database, "engine");
415
- const engineBruto = valorCampoCompleto(campoEngine);
416
- if (!engineBruto) {
417
- diagnosticos.push(criarDiagnostico("SEM101", `Database "${database.nome ?? "database"}" precisa declarar engine.`, "erro", database.intervalo, "Use postgres, mysql, sqlite, mongodb ou redis."));
418
- return;
419
- }
420
- const engine = normalizarEngineBanco(engineBruto);
421
- if (!engine) {
422
- diagnosticos.push(criarDiagnostico("SEM102", `Database "${database.nome ?? "database"}" declarou engine invalido: "${engineBruto}".`, "erro", campoEngine?.intervalo ?? database.intervalo, "Use postgres, mysql, sqlite, mongodb ou redis."));
423
- return;
424
- }
425
- const consistency = valorCampoCompleto(localizarCampo(database, "consistency"));
426
- const durability = valorCampoCompleto(localizarCampo(database, "durability"));
427
- const transactionModel = valorCampoCompleto(localizarCampo(database, "transaction_model"));
428
- const queryModel = valorCampoCompleto(localizarCampo(database, "query_model"));
429
- const portavel = valorCampoCompleto(localizarCampo(database, "portavel"));
430
- if (consistency && !normalizarConsistenciaPersistencia(consistency)) {
431
- diagnosticos.push(criarDiagnostico("SEM103", `Database "${database.nome ?? "database"}" declarou consistency invalida: "${consistency}".`, "erro", localizarCampo(database, "consistency")?.intervalo, "Use eventual, forte, serializable, snapshot ou causal."));
432
- }
433
- if (durability && !normalizarDurabilidadePersistencia(durability)) {
434
- diagnosticos.push(criarDiagnostico("SEM104", `Database "${database.nome ?? "database"}" declarou durability invalida: "${durability}".`, "erro", localizarCampo(database, "durability")?.intervalo, "Use baixa, media ou alta."));
435
- }
436
- if (transactionModel && !normalizarModeloTransacaoPersistencia(transactionModel)) {
437
- diagnosticos.push(criarDiagnostico("SEM105", `Database "${database.nome ?? "database"}" declarou transaction_model invalido: "${transactionModel}".`, "erro", localizarCampo(database, "transaction_model")?.intervalo, "Use mvcc, bloqueio, documento, otimista ou single_thread."));
438
- }
439
- if (queryModel && !normalizarModeloConsultaPersistencia(queryModel)) {
440
- diagnosticos.push(criarDiagnostico("SEM115", `Database "${database.nome ?? "database"}" declarou query_model invalido: "${queryModel}".`, "erro", localizarCampo(database, "query_model")?.intervalo, "Use sql, documento, chave_valor, pipeline ou stream."));
441
- }
442
- validarBooleanoPersistencia(portavel, localizarCampo(database, "portavel")?.intervalo ?? database.intervalo, diagnosticos, "SEM116", `Database "${database.nome ?? "database"}" declarou portavel com valor invalido: "${portavel}".`);
443
- for (const recurso of database.blocos) {
444
- if (recurso.tipo !== "bloco_generico") {
445
- continue;
446
- }
447
- validarRecursoPersistencia(database, recurso, diagnosticos);
448
- }
449
- }
450
- function coletarSuperficiesModulo(modulo) {
451
- return [
452
- ...modulo.workers.map((superficie) => ({ tipo: "worker", superficie })),
453
- ...modulo.eventos.map((superficie) => ({ tipo: "evento", superficie })),
454
- ...modulo.filas.map((superficie) => ({ tipo: "fila", superficie })),
455
- ...modulo.crons.map((superficie) => ({ tipo: "cron", superficie })),
456
- ...modulo.webhooks.map((superficie) => ({ tipo: "webhook", superficie })),
457
- ...modulo.caches.map((superficie) => ({ tipo: "cache", superficie })),
458
- ...modulo.storages.map((superficie) => ({ tipo: "storage", superficie })),
459
- ...modulo.policies.map((superficie) => ({ tipo: "policy", superficie })),
460
- ];
461
- }
462
- function superficieEhPublica(superficie, tipoSuperficie) {
463
- return extrairPerfilCompatibilidade(superficie, tipoSuperficie === "webhook" ? "publico" : "interno") === "publico";
464
- }
465
- function taskEhSensivel(task) {
466
- const criticidadeOperacional = task.execucao
467
- ? valorCampoCompleto(localizarCampo(task.execucao, "criticidade_operacional"))
468
- : undefined;
469
- if (criticidadeOperacional === "alta" || criticidadeOperacional === "critica") {
470
- return true;
471
- }
472
- return (task.effects?.linhas ?? []).some((linha) => {
473
- const efeito = parsearEfeitoSemantico(linha.conteudo);
474
- if (!efeito) {
475
- return false;
476
- }
477
- return efeito.categoria === "persistencia" || efeito.criticidade === "alta" || efeito.criticidade === "critica" || efeitoEhPrivilegiado(efeito);
478
- });
479
- }
480
- function taskTemRastreabilidade(task) {
481
- return Boolean(task.impl || task.vinculos);
482
- }
483
- function routeEhMutante(route) {
484
- const metodo = (localizarCampo(route.corpo, "metodo")?.valor ?? "").toUpperCase();
485
- return ["POST", "PUT", "PATCH", "DELETE"].includes(metodo);
486
- }
487
- function validarExecucaoBloco(execucao, diagnosticos, contexto) {
488
- if (!execucao) {
489
- return;
490
- }
491
- for (const campo of execucao.campos) {
492
- if (!CAMPOS_EXECUCAO_SUPORTADOS.has(campo.nome)) {
493
- diagnosticos.push(criarDiagnostico("SEM065", `Campo de execucao "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use apenas idempotencia, timeout, retry, compensacao ou criticidade_operacional."));
494
- continue;
495
- }
496
- if (campo.nome === "criticidade_operacional") {
497
- const criticidade = valorCampoCompleto(campo);
498
- if (criticidade && !CRITICIDADES_OPERACIONAIS.has(criticidade)) {
499
- diagnosticos.push(criarDiagnostico("SEM066", `Execucao de ${contexto} declarou criticidade_operacional invalida: "${criticidade}".`, "erro", campo.intervalo, "Use apenas baixa, media, alta ou critica em execucao."));
500
- }
501
- }
502
- }
503
- }
504
- function validarExecucao(task, diagnosticos) {
505
- validarExecucaoBloco(task.execucao, diagnosticos, `task "${task.nome}"`);
506
- }
507
- function validarAuthBloco(bloco, diagnosticos, contexto) {
508
- if (!bloco) {
509
- return;
510
- }
511
- for (const campo of bloco.campos) {
512
- if (!CAMPOS_AUTH_SUPORTADOS.has(campo.nome)) {
513
- diagnosticos.push(criarDiagnostico("SEM074", `Campo de auth "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use apenas modo, estrategia, principal ou origem em auth."));
514
- }
515
- }
516
- const auth = extrairContratoAuth(bloco);
517
- if (auth.modo && !MODOS_AUTH_SUPORTADOS.has(auth.modo)) {
518
- diagnosticos.push(criarDiagnostico("SEM075", `Auth em ${contexto} declarou modo invalido: "${auth.modo}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional, anonimo, interno ou m2m."));
519
- }
520
- if (auth.principal && !PRINCIPAIS_AUTH_SUPORTADOS.has(auth.principal)) {
521
- diagnosticos.push(criarDiagnostico("SEM076", `Auth em ${contexto} declarou principal invalido: "${auth.principal}".`, "erro", bloco.intervalo, "Use usuario, servico, sistema ou anonimo."));
522
- }
523
- if (auth.origem && !ORIGENS_AUTH_SUPORTADAS.has(auth.origem)) {
524
- diagnosticos.push(criarDiagnostico("SEM077", `Auth em ${contexto} declarou origem invalida: "${auth.origem}".`, "erro", bloco.intervalo, "Use publica, interna, worker, webhook, fila ou cron."));
525
- }
526
- }
527
- function validarAuthzBloco(bloco, diagnosticos, contexto) {
528
- if (!bloco) {
529
- return;
530
- }
531
- for (const campo of bloco.campos) {
532
- if (!CAMPOS_AUTHZ_SUPORTADOS.has(campo.nome)) {
533
- diagnosticos.push(criarDiagnostico("SEM078", `Campo de authz "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use papel, papeis, escopo, escopos, politica ou tenant em authz."));
534
- }
535
- }
536
- const authz = extrairContratoAuthz(bloco);
537
- if (authz.tenant && !TENANTS_AUTHZ_SUPORTADOS.has(authz.tenant)) {
538
- diagnosticos.push(criarDiagnostico("SEM079", `Authz em ${contexto} declarou tenant invalido: "${authz.tenant}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional ou isolado."));
539
- }
540
- if (authz.papeis.length === 0 && authz.escopos.length === 0 && !authz.politica) {
541
- diagnosticos.push(criarDiagnostico("SEM080", `Authz em ${contexto} precisa declarar papeis, escopos ou politica.`, "erro", bloco.intervalo, "Explicite ao menos um papel, escopo ou politica para a autorizacao nao virar enfeite."));
542
- }
543
- }
544
- function validarDadosBloco(bloco, diagnosticos, contexto) {
545
- if (!bloco) {
546
- return;
547
- }
548
- for (const campo of bloco.campos) {
549
- const valor = valorCampoCompleto(campo);
550
- if (CAMPOS_DADOS_SUPORTADOS.has(campo.nome)) {
551
- continue;
552
- }
553
- if (valor && !CLASSIFICACOES_DADO_SUPORTADAS.has(valor)) {
554
- diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao invalida para "${campo.nome}": "${valor}".`, "erro", campo.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
555
- }
556
- }
557
- const dados = extrairContratoDados(bloco);
558
- if (dados.classificacaoPadrao && !CLASSIFICACOES_DADO_SUPORTADAS.has(dados.classificacaoPadrao)) {
559
- diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao_padrao invalida: "${dados.classificacaoPadrao}".`, "erro", bloco.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
560
- }
561
- if (dados.redacaoLog && !REDACOES_LOG_SUPORTADAS.has(dados.redacaoLog)) {
562
- diagnosticos.push(criarDiagnostico("SEM082", `Dados em ${contexto} declarou redacao_log invalida: "${dados.redacaoLog}".`, "erro", bloco.intervalo, "Use livre, parcial, obrigatoria ou proibida."));
563
- }
564
- for (const subbloco of bloco.blocos) {
565
- if (subbloco.tipo !== "bloco_generico") {
566
- continue;
567
- }
568
- const nomeSubbloco = subbloco.nome ?? subbloco.palavraChave;
569
- if (nomeSubbloco !== "input" && nomeSubbloco !== "output") {
570
- diagnosticos.push(criarDiagnostico("SEM083", `Dados em ${contexto} nao suporta o subbloco "${nomeSubbloco}".`, "erro", subbloco.intervalo, "Use apenas campos diretos ou subblocos input/output para classificar dados."));
571
- continue;
572
- }
573
- for (const campo of subbloco.campos) {
574
- const classificacao = valorCampoCompleto(campo);
575
- if (classificacao && !CLASSIFICACOES_DADO_SUPORTADAS.has(classificacao)) {
576
- diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao invalida para "${nomeSubbloco}.${campo.nome}": "${classificacao}".`, "erro", campo.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
577
- }
578
- }
579
- }
580
- }
581
- function validarAuditBloco(bloco, diagnosticos, contexto) {
582
- if (!bloco) {
583
- return;
584
- }
585
- for (const campo of bloco.campos) {
586
- if (!CAMPOS_AUDIT_SUPORTADOS.has(campo.nome)) {
587
- diagnosticos.push(criarDiagnostico("SEM084", `Campo de audit "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use evento, ator, correlacao, retencao ou motivo em audit."));
588
- }
589
- }
590
- const audit = extrairContratoAudit(bloco);
591
- if (!audit.evento) {
592
- diagnosticos.push(criarDiagnostico("SEM085", `Audit em ${contexto} precisa declarar evento.`, "erro", bloco.intervalo, "Explique qual evento auditavel sera registrado para a operacao."));
593
- }
594
- if (audit.motivo && !MOTIVOS_AUDIT_SUPORTADOS.has(audit.motivo)) {
595
- diagnosticos.push(criarDiagnostico("SEM086", `Audit em ${contexto} declarou motivo invalido: "${audit.motivo}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional ou dispensado."));
596
- }
597
- }
598
- function validarSegredosBloco(bloco, diagnosticos, contexto) {
599
- if (!bloco) {
600
- return;
601
- }
602
- const segredos = extrairContratoSegredos(bloco);
603
- if (segredos.itens.length === 0) {
604
- diagnosticos.push(criarDiagnostico("SEM087", `Segredos em ${contexto} precisa declarar ao menos um segredo nomeado.`, "erro", bloco.intervalo, "Use segredos { nome_do_segredo { origem: vault escopo: runtime ... } }."));
605
- return;
606
- }
607
- for (const item of bloco.blocos) {
608
- if (item.tipo !== "bloco_generico") {
609
- continue;
610
- }
611
- for (const campo of item.campos) {
612
- if (!CAMPOS_SEGREDO_SUPORTADOS.has(campo.nome)) {
613
- diagnosticos.push(criarDiagnostico("SEM087", `Segredo "${item.nome ?? item.palavraChave}" em ${contexto} usa o campo "${campo.nome}", que nao e suportado.`, "erro", campo.intervalo, "Use origem, escopo, acesso, rotacao, nao_logar, nao_retornar ou mascarar."));
614
- }
615
- }
616
- const nomeSegredo = item.nome ?? item.palavraChave;
617
- const origem = valorCampoCompleto(localizarCampo(item, "origem"));
618
- if (!origem) {
619
- diagnosticos.push(criarDiagnostico("SEM088", `Segredo "${nomeSegredo}" em ${contexto} precisa declarar origem.`, "erro", item.intervalo, "Explicite a origem do segredo, como vault, env, secret_manager ou runtime."));
620
- }
621
- for (const nomeBooleano of ["nao_logar", "nao_retornar", "mascarar"]) {
622
- const campo = localizarCampo(item, nomeBooleano);
623
- const valor = valorCampoCompleto(campo);
624
- if (campo && valor !== "verdadeiro" && valor !== "true" && valor !== "falso" && valor !== "false") {
625
- diagnosticos.push(criarDiagnostico("SEM089", `Segredo "${nomeSegredo}" em ${contexto} declarou "${nomeBooleano}" com valor invalido: "${valor}".`, "erro", campo.intervalo, "Use verdadeiro/falso para campos booleanos de segredos."));
626
- }
627
- }
628
- }
629
- }
630
- function validarForbiddenBloco(bloco, efeitos, diagnosticos, contexto) {
631
- const forbidden = extrairContratoForbidden(bloco);
632
- if (!bloco) {
633
- return forbidden;
634
- }
635
- for (const regra of forbidden.regras) {
636
- if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/u.test(regra)) {
637
- diagnosticos.push(criarDiagnostico("SEM090", `Forbidden em ${contexto} declarou regra invalida: "${regra}".`, "erro", bloco.intervalo, "Use regras simples como network.egress, shell.exec, retorno.credencial ou log.segredo."));
638
- }
639
- }
640
- for (const linha of efeitos) {
641
- const efeito = parsearEfeitoSemantico(linha.conteudo);
642
- if (efeito && forbiddenContemRegra(forbidden, efeito.categoria)) {
643
- diagnosticos.push(criarDiagnostico("SEM091", `Forbidden em ${contexto} proibe "${efeito.categoria}", mas effects ainda declara esse efeito.`, "erro", linha.intervalo, "Remova o efeito proibido ou ajuste o bloco forbidden para refletir a operacao permitida de verdade."));
644
- }
645
- }
646
- return forbidden;
647
- }
648
- function coletarPerfilSegurancaDeclarado(corpo, effects, diagnosticos, contexto) {
649
- const authBloco = localizarBloco(corpo, "auth");
650
- const authzBloco = localizarBloco(corpo, "authz");
651
- const dadosBloco = localizarBloco(corpo, "dados");
652
- const auditBloco = localizarBloco(corpo, "audit");
653
- const segredosBloco = localizarBloco(corpo, "segredos");
654
- const forbiddenBloco = localizarBloco(corpo, "forbidden");
655
- if (diagnosticos) {
656
- validarAuthBloco(authBloco, diagnosticos, contexto);
657
- validarAuthzBloco(authzBloco, diagnosticos, contexto);
658
- validarDadosBloco(dadosBloco, diagnosticos, contexto);
659
- validarAuditBloco(auditBloco, diagnosticos, contexto);
660
- validarSegredosBloco(segredosBloco, diagnosticos, contexto);
661
- }
662
- const forbidden = diagnosticos
663
- ? validarForbiddenBloco(forbiddenBloco, effects?.linhas ?? [], diagnosticos, contexto)
664
- : extrairContratoForbidden(forbiddenBloco);
665
- const auth = extrairContratoAuth(authBloco);
666
- const authz = extrairContratoAuthz(authzBloco);
667
- const dados = extrairContratoDados(dadosBloco);
668
- const audit = extrairContratoAudit(auditBloco);
669
- const segredos = extrairContratoSegredos(segredosBloco);
670
- const efeitosEstruturados = (effects?.linhas ?? [])
671
- .map((linha) => parsearEfeitoSemantico(linha.conteudo))
672
- .filter((efeito) => Boolean(efeito));
673
- return {
674
- auth,
675
- authz,
676
- dados,
677
- audit,
678
- segredos,
679
- forbidden,
680
- efeitoPrivilegiado: efeitosEstruturados.some((efeito) => efeitoEhPrivilegiado(efeito)),
681
- dadosSensiveis: contratoDadosTemSensivel(dados),
682
- exigeSegredos: efeitosEstruturados.some((efeito) => efeitoRequerSegredo(efeito)) || contratoDadosTemSegredoOuCredencial(dados),
683
- };
684
- }
685
- function emitirGuardrailsSeguranca(contexto, intervalo, perfil, diagnosticos, opcoes) {
686
- const exigeAuth = opcoes.publico;
687
- const exigeAuthz = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
688
- const exigeDados = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado;
689
- const exigeAudit = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
690
- const exigeSegredos = perfil.exigeSegredos;
691
- const exigeForbidden = perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
692
- if (exigeAuth && !perfil.auth.explicita) {
693
- diagnosticos.push(criarDiagnostico("SEM094", `${contexto} deveria declarar auth explicita para reduzir ambiguidade de seguranca na borda publica.`, "aviso", intervalo, "Declare auth { modo: obrigatorio|anonimo ... } para deixar a intencao da exposicao publica cristalina."));
694
- }
695
- if (exigeAuthz && !perfil.authz.explicita) {
696
- diagnosticos.push(criarDiagnostico("SEM095", `${contexto} deveria declarar authz explicita porque opera com risco, privilegio ou exposicao publica.`, "aviso", intervalo, "Declare papeis, escopos ou politica em authz para nao empurrar autorizacao para o limbo do codigo vivo."));
697
- }
698
- if (exigeDados && !perfil.dados.explicita) {
699
- diagnosticos.push(criarDiagnostico("SEM096", `${contexto} deveria classificar dados de forma explicita em dados { ... }.`, "aviso", intervalo, "Classifique input/output com publico, interno, pii, financeiro, credencial ou segredo."));
700
- }
701
- if (exigeAudit && !perfil.audit.explicita) {
702
- diagnosticos.push(criarDiagnostico("SEM097", `${contexto} deveria declarar audit explicita para operar com trilha semantica de seguranca.`, "aviso", intervalo, "Declare audit { evento: ... correlacao: ... motivo: ... } para nao depender de adivinhacao operacional."));
703
- }
704
- if (exigeSegredos && !perfil.segredos.explicita) {
705
- diagnosticos.push(criarDiagnostico("SEM098", `${contexto} deveria declarar segredos explicitos porque toca credencial, segredo ou secret.read.`, "aviso", intervalo, "Use segredos { nome { origem: vault escopo: runtime ... } } para governar acesso sensivel."));
706
- }
707
- if (exigeForbidden && !perfil.forbidden.explicita) {
708
- diagnosticos.push(criarDiagnostico("SEM099", `${contexto} deveria declarar forbidden explicito para proibir operacoes perigosas ou vazamento semantico.`, "aviso", intervalo, "Use forbidden { network.egress shell.exec log.segredo retorno.credencial } conforme o risco da operacao."));
709
- }
710
- }
711
- function validarErroOperacional(task, diagnosticos) {
712
- if (!task.error) {
713
- return;
714
- }
715
- const nomes = new Set();
716
- for (const campo of task.error.campos) {
717
- if (nomes.has(campo.nome)) {
718
- diagnosticos.push(diagnosticoDuplicado(campo.nome, "Erro", campo.intervalo));
719
- }
720
- nomes.add(campo.nome);
721
- }
722
- for (const bloco of task.error.blocos) {
723
- if (bloco.tipo !== "bloco_generico") {
724
- continue;
725
- }
726
- const codigo = bloco.nome ?? bloco.palavraChave;
727
- if (!codigo || codigo === "desconhecido") {
728
- continue;
729
- }
730
- if (nomes.has(codigo)) {
731
- diagnosticos.push(diagnosticoDuplicado(codigo, "Erro", bloco.intervalo));
732
- }
733
- nomes.add(codigo);
734
- const mensagem = localizarCampo(bloco, "mensagem");
735
- if (!mensagem) {
736
- diagnosticos.push(criarDiagnostico("SEM067", `Erro estruturado "${codigo}" da task "${task.nome}" precisa declarar mensagem.`, "erro", bloco.intervalo, "Use error { codigo { mensagem: \"...\" categoria: dominio ... } }."));
737
- }
738
- for (const campo of bloco.campos) {
739
- if (!CAMPOS_ERRO_OPERACIONAL.has(campo.nome)) {
740
- diagnosticos.push(criarDiagnostico("SEM068", `Erro estruturado "${codigo}" da task "${task.nome}" usa o campo "${campo.nome}", que nao e suportado.`, "erro", campo.intervalo, "Use mensagem, categoria, recuperabilidade, acao_chamador, impacta_estado ou requer_compensacao."));
741
- }
742
- }
743
- }
744
- }
745
- function validarSuperficie(superficie, tipoSuperficie, tasksConhecidas, tiposConhecidos, diagnosticos) {
746
- const nomeSuperficie = superficie.nome ?? tipoSuperficie;
747
- const task = localizarCampo(superficie, "task", "tarefa");
748
- const input = localizarBloco(superficie, "input");
749
- const output = localizarBloco(superficie, "output");
750
- const effects = localizarBloco(superficie, "effects");
751
- const impl = localizarBloco(superficie, "impl");
752
- const vinculos = localizarBloco(superficie, "vinculos");
753
- const execucao = localizarBloco(superficie, "execucao");
754
- if (!task && !impl && !vinculos) {
755
- diagnosticos.push(criarDiagnostico("SEM069", `Superficie ${tipoSuperficie} "${nomeSuperficie}" precisa declarar task, impl ou vinculos para nao virar bloco decorativo.`, "erro", superficie.intervalo, "Declare ao menos uma task associada, um impl explicito ou vinculos rastreaveis com codigo vivo."));
756
- }
757
- if (task && !tasksConhecidas.has(task.valor)) {
758
- diagnosticos.push(criarDiagnostico("SEM070", `Superficie ${tipoSuperficie} "${nomeSuperficie}" referencia task "${task.valor}" que nao existe.`, "erro", task.intervalo, "Ajuste a task para apontar para uma task declarada ou importada."));
759
- }
760
- if (input) {
761
- validarCamposDeTipos(input.campos, tiposConhecidos, diagnosticos, `input da superficie ${tipoSuperficie} ${nomeSuperficie}`);
762
- }
763
- if (output) {
764
- validarCamposDeTipos(output.campos, tiposConhecidos, diagnosticos, `output da superficie ${tipoSuperficie} ${nomeSuperficie}`);
765
- }
766
- if (effects) {
767
- validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects da superficie ${tipoSuperficie} ${nomeSuperficie}`);
768
- }
769
- validarExecucaoBloco(execucao, diagnosticos, `superficie ${tipoSuperficie} "${nomeSuperficie}"`);
770
- validarVinculos(vinculos, diagnosticos, `${tipoSuperficie} ${nomeSuperficie}`);
771
- const perfilSeguranca = coletarPerfilSegurancaDeclarado(superficie, effects, diagnosticos, `superficie ${tipoSuperficie} "${nomeSuperficie}"`);
772
- emitirGuardrailsSeguranca(`Superficie ${tipoSuperficie} "${nomeSuperficie}"`, superficie.intervalo, perfilSeguranca, diagnosticos, {
773
- publico: superficieEhPublica(superficie, tipoSuperficie),
774
- sensivel: perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis,
775
- });
776
- }
777
- function recomporCaminhoRoute(campo) {
778
- if (!campo) {
779
- return undefined;
780
- }
781
- return [campo.valor, ...campo.modificadores]
782
- .join(" ")
783
- .replace(/\s*\/\s*/g, "/")
784
- .trim();
785
- }
786
- function serializarTransicao(origem, destino) {
787
- return `${origem}->${destino}`;
788
- }
789
- function descreverSugestoes(valores, prefixo) {
790
- const lista = [...new Set([...valores].filter(Boolean))].sort((a, b) => a.localeCompare(b, "pt-BR"));
791
- if (lista.length === 0) {
792
- return undefined;
793
- }
794
- const recorte = lista.slice(0, 6).join(", ");
795
- return `${prefixo}: ${recorte}${lista.length > 6 ? ", ..." : ""}.`;
796
- }
797
- function listarCandidatosUseRelativo(moduloAtual, caminhoImportado) {
798
- const segmentos = moduloAtual.split(".").filter(Boolean);
799
- const caminhoNormalizado = caminhoImportado.replace(/^\.+/u, "").trim();
800
- if (!caminhoNormalizado || segmentos.length <= 1) {
801
- return [];
802
- }
803
- const candidatos = [];
804
- for (let tamanho = segmentos.length - 1; tamanho >= 1; tamanho -= 1) {
805
- const candidato = [...segmentos.slice(0, tamanho), caminhoNormalizado].join(".");
806
- if (candidato !== caminhoImportado) {
807
- candidatos.push(candidato);
808
- }
809
- }
810
- return [...new Set(candidatos)];
811
- }
812
- function resolverUseSema(moduloAtual, caminhoImportado, contextosModulos) {
813
- if (!contextosModulos) {
814
- return { caminhoResolvido: undefined, candidatosRelativos: [] };
815
- }
816
- if (contextosModulos.has(caminhoImportado)) {
817
- return { caminhoResolvido: caminhoImportado, candidatosRelativos: [] };
818
- }
819
- const candidatosRelativos = listarCandidatosUseRelativo(moduloAtual, caminhoImportado);
820
- const caminhoResolvido = candidatosRelativos.find((candidato) => contextosModulos.has(candidato));
821
- return { caminhoResolvido, candidatosRelativos };
822
- }
823
- function descreverDicaSintaxeExpressao(texto, dicaPadrao) {
824
- const normalizado = texto.trim();
825
- if (/\sou\s/u.test(normalizado)
826
- && (normalizado.includes("\"")
827
- || normalizado.includes("'")
828
- || /^[A-Za-z_][A-Za-z0-9_.]*\s*(==|!=|>|<|>=|<=)\s+.+\s+ou\s+.+$/u.test(normalizado))
829
- && !/\bem\s+\[/u.test(normalizado)) {
830
- const alvo = normalizado.match(/^([A-Za-z_][A-Za-z0-9_.]*)\s*(==|!=|>|<|>=|<=)/u)?.[1];
831
- if (alvo) {
832
- return `${dicaPadrao} Se a ideia era comparar ${alvo} contra varios valores, repita o campo em cada comparacao ou prefira "${alvo} em [A, B]".`;
833
- }
834
- return `${dicaPadrao} Se a ideia era comparar um campo contra varios valores, repita o campo em cada comparacao ou prefira "campo em [A, B]".`;
835
- }
836
- return dicaPadrao;
837
- }
838
- function validarExpressoesDeclaradas(linhas, diagnosticos, contexto) {
839
- for (const linha of linhas) {
840
- const expressao = parsearExpressaoSemantica(linha.conteudo);
841
- if (!expressao) {
842
- diagnosticos.push(criarDiagnostico(contexto.codigoErroSintaxe, `Declaracao invalida em ${contexto.nomeBloco}: "${linha.conteudo}".`, "erro", linha.intervalo, descreverDicaSintaxeExpressao(linha.conteudo, contexto.dicaSintaxe)));
843
- continue;
844
- }
845
- for (const referencia of extrairReferenciasDaExpressao(expressao)) {
846
- const raiz = extrairRaiz(referencia);
847
- const referenciaPermitida = contexto.simbolosPermitidos.has(raiz) || (contexto.aceitarMarcadoresSemanticos && ehMarcadorSemantico(raiz));
848
- if (!referenciaPermitida) {
849
- 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));
850
- }
851
- }
852
- }
853
- }
854
- function validarEfeitosDeclarados(linhas, diagnosticos, contexto) {
855
- for (const linha of linhas) {
856
- const efeito = parsearEfeitoSemantico(linha.conteudo);
857
- if (!efeito) {
858
- diagnosticos.push(criarDiagnostico("SEM023", `Declaracao invalida de efeito em ${contexto}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"categoria alvo\" ou \"categoria alvo detalhe\", podendo adicionar criticidade=..., privilegio=... e isolamento=...."));
859
- continue;
860
- }
861
- if (!ehCategoriaEfeitoSemantico(efeito.categoria)) {
862
- diagnosticos.push(criarDiagnostico("SEM048", `Categoria de efeito "${efeito.categoria}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use categorias como persistencia, consulta, evento, auditoria, db.write, queue.publish, fs.write, network.egress, secret.read ou shell.exec."));
863
- }
864
- if (efeito.criticidadeTexto && !ehCriticidadeEfeitoSemantico(efeito.criticidadeTexto)) {
865
- 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."));
866
- }
867
- if (efeito.privilegioTexto
868
- && !["leitura", "escrita", "publicacao", "execucao", "admin", "egress"].includes(efeito.privilegioTexto)) {
869
- diagnosticos.push(criarDiagnostico("SEM092", `Privilegio de efeito "${efeito.privilegioTexto}" nao e suportado em ${contexto}.`, "erro", linha.intervalo, "Use privilegio=leitura, privilegio=escrita, privilegio=publicacao, privilegio=execucao, privilegio=admin ou privilegio=egress."));
870
- }
871
- if (efeito.isolamentoTexto
872
- && !["tenant", "processo", "host", "vps", "global"].includes(efeito.isolamentoTexto)) {
873
- diagnosticos.push(criarDiagnostico("SEM093", `Isolamento de efeito "${efeito.isolamentoTexto}" nao e suportado em ${contexto}.`, "erro", linha.intervalo, "Use isolamento=tenant, isolamento=processo, isolamento=host, isolamento=vps ou isolamento=global."));
874
- }
875
- }
876
- }
877
- function validarState(state, tiposConhecidos, enumsConhecidos, diagnosticos) {
878
- const possuiConteudo = state.corpo.campos.length > 0 || state.corpo.linhas.length > 0 || state.corpo.blocos.length > 0;
879
- if (!possuiConteudo) {
880
- 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."));
881
- }
882
- validarCamposDeTipos(state.corpo.campos, tiposConhecidos, diagnosticos, `state ${state.nome ?? "<anonimo>"}`);
883
- const fields = localizarBloco(state.corpo, "fields");
884
- if (fields) {
885
- validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `fields do state ${state.nome ?? "<anonimo>"}`);
886
- }
887
- const nomesCampos = new Set([
888
- ...state.corpo.campos.map((campo) => campo.nome),
889
- ...(fields?.campos ?? []).map((campo) => campo.nome),
890
- ]);
891
- const invariants = localizarBloco(state.corpo, "invariants");
892
- if (invariants) {
893
- validarExpressoesDeclaradas(invariants.linhas, diagnosticos, {
894
- codigoErroSintaxe: "SEM024",
895
- codigoErroReferencia: "SEM025",
896
- nomeBloco: `invariants do state ${state.nome ?? "<anonimo>"}`,
897
- simbolosPermitidos: nomesCampos,
898
- dicaSintaxe: "Use expressoes como \"campo existe\", \"campo == valor\" ou \"campo em [A, B]\".",
899
- dicaReferencia: "Referencie apenas campos do proprio state nas invariantes.",
900
- });
901
- }
902
- const transitions = localizarBloco(state.corpo, "transitions");
903
- if (transitions) {
904
- const campoTransicao = (fields?.campos ?? []).find((campo) => campo.nome === "status" || campo.nome === "estado")
905
- ?? state.corpo.campos.find((campo) => campo.nome === "status" || campo.nome === "estado");
906
- if (!campoTransicao) {
907
- 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."));
908
- }
909
- const enumValores = campoTransicao ? enumsConhecidos.get(campoTransicao.valor) : undefined;
910
- for (const linha of transitions.linhas) {
911
- const transicao = parsearTransicaoEstado(linha.conteudo);
912
- if (!transicao) {
913
- diagnosticos.push(criarDiagnostico("SEM027", `Transicao invalida em state ${state.nome ?? "<anonimo>"}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"ORIGEM -> DESTINO\" para declarar transicoes."));
914
- continue;
915
- }
916
- if (enumValores) {
917
- if (!enumValores.has(transicao.origem)) {
918
- 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."));
919
- }
920
- if (!enumValores.has(transicao.destino)) {
921
- 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."));
922
- }
923
- }
924
- }
925
- }
926
- }
927
- function validarInvariantesDeCampos(bloco, nomeBloco, diagnosticos) {
928
- const fields = localizarBloco(bloco, "fields");
929
- const nomesCampos = new Set([
930
- ...bloco.campos.map((campo) => campo.nome),
931
- ...(fields?.campos ?? []).map((campo) => campo.nome),
932
- ]);
933
- const invariants = localizarBloco(bloco, "invariants");
934
- if (!invariants) {
935
- return;
936
- }
937
- validarExpressoesDeclaradas(invariants.linhas, diagnosticos, {
938
- codigoErroSintaxe: "SEM062",
939
- codigoErroReferencia: "SEM063",
940
- nomeBloco: `invariants de ${nomeBloco}`,
941
- simbolosPermitidos: nomesCampos,
942
- dicaSintaxe: "Use expressoes como \"campo existe\", \"campo == valor\" ou \"campo em [A, B]\".",
943
- dicaReferencia: "Referencie apenas campos declarados no proprio bloco ao escrever invariantes de dominio.",
944
- });
945
- }
946
- function referenciaAgentConhecida(referencia, agentsConhecidos) {
947
- if (agentsConhecidos.has(referencia)) {
948
- return true;
949
- }
950
- return referencia.startsWith("agent.") && agentsConhecidos.has(referencia.slice("agent.".length));
951
- }
952
- function referenciaOperadorFlowConhecida(referencia, tasksConhecidas, agentsConhecidos) {
953
- return tasksConhecidas.has(referencia) || referenciaAgentConhecida(referencia, agentsConhecidos);
954
- }
955
- function validarFlow(flow, tasksConhecidas, agentsConhecidos, tarefasDetalhadas, diagnosticos) {
956
- const possuiEtapas = flow.corpo.linhas.length > 0 || flow.corpo.campos.length > 0 || flow.corpo.blocos.length > 0;
957
- if (!possuiEtapas) {
958
- 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."));
959
- }
960
- const effects = localizarBloco(flow.corpo, "effects");
961
- if (effects) {
962
- validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects do flow ${flow.nome}`);
963
- }
964
- validarVinculos(flow.vinculos, diagnosticos, `flow ${flow.nome}`);
965
- const tarefasReferenciadas = flow.corpo.campos
966
- .filter((campo) => campo.nome === "task" || campo.nome === "tarefa")
967
- .map((campo) => campo.valor);
968
- for (const tarefa of tarefasReferenciadas) {
969
- if (!referenciaOperadorFlowConhecida(tarefa, tasksConhecidas, agentsConhecidos)) {
970
- diagnosticos.push(criarDiagnostico("SEM013", `Flow "${flow.nome}" referencia operador "${tarefa}" que nao existe.`, "erro", flow.intervalo, "Declare a task ou agent no mesmo modulo, ou ajuste a referencia do flow."));
971
- }
972
- }
973
- const etapas = flow.corpo.linhas
974
- .map((linha) => ({ linha, etapa: parsearEtapaFlow(linha.conteudo) }))
975
- .filter((item) => item.linha.conteudo.trim().startsWith("etapa "));
976
- const nomesEtapas = new Set();
977
- const contextoFlow = new Set(flow.corpo.campos.map((campo) => campo.nome));
978
- const etapasValidas = new Map();
979
- for (const item of etapas) {
980
- if (!item.etapa) {
981
- 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\"."));
982
- continue;
983
- }
984
- if (nomesEtapas.has(item.etapa.nome)) {
985
- 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."));
986
- continue;
987
- }
988
- nomesEtapas.add(item.etapa.nome);
989
- etapasValidas.set(item.etapa.nome, item.etapa);
990
- const etapaUsaTask = Boolean(item.etapa.task && tasksConhecidas.has(item.etapa.task));
991
- const etapaUsaAgent = Boolean(item.etapa.task && referenciaAgentConhecida(item.etapa.task, agentsConhecidos));
992
- if (item.etapa.task && !etapaUsaTask && !etapaUsaAgent) {
993
- const sugestoesOperadores = [
994
- descreverSugestoes(tasksConhecidas, "Tasks conhecidas no contexto"),
995
- descreverSugestoes(agentsConhecidos, "Agents conhecidos no contexto"),
996
- ].filter(Boolean).join(" ");
997
- diagnosticos.push(criarDiagnostico("SEM034", `Etapa "${item.etapa.nome}" do flow "${flow.nome}" usa operador "${item.etapa.task}" que nao existe.`, "erro", item.linha.intervalo, sugestoesOperadores || "Ajuste a etapa para apontar para uma task ou agent declarado/importado."));
998
- }
999
- if (item.etapa.task && etapaUsaTask) {
1000
- const detalhesTask = tarefasDetalhadas.get(item.etapa.task);
1001
- if (detalhesTask) {
1002
- const indiceInput = indicesCampos(detalhesTask.input);
1003
- for (const mapeamento of item.etapa.mapeamentos) {
1004
- const campoInput = indiceInput.get(mapeamento.campo);
1005
- if (!campoInput) {
1006
- 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."));
1007
- }
1008
- const raizValor = extrairRaiz(mapeamento.valor);
1009
- const ehLiteral = ["verdadeiro", "falso", "nulo"].includes(mapeamento.valor)
1010
- || /^-?\d+(?:\.\d+)?$/.test(mapeamento.valor)
1011
- || /^".*"$/.test(mapeamento.valor);
1012
- const aceitaLiteralTextual = Boolean(campoInput
1013
- && ["Texto", "Id", "Email", "Url"].includes(campoInput.tipo)
1014
- && !mapeamento.valor.includes(".")
1015
- && !contextoFlow.has(raizValor)
1016
- && !nomesEtapas.has(raizValor));
1017
- if (!ehLiteral && !aceitaLiteralTextual && !contextoFlow.has(raizValor) && !nomesEtapas.has(raizValor)) {
1018
- const sugestoesContexto = [
1019
- descreverSugestoes(contextoFlow, "Campos do flow"),
1020
- descreverSugestoes(nomesEtapas, "Etapas conhecidas"),
1021
- ].filter(Boolean).join(" ");
1022
- 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."));
1023
- }
1024
- if (mapeamento.valor.includes(".")) {
1025
- const [etapaOrigem, campoSaida] = mapeamento.valor.split(".", 2);
1026
- const etapaReferenciada = etapaOrigem ? etapasValidas.get(etapaOrigem) : undefined;
1027
- const taskReferenciada = etapaReferenciada?.task ? tarefasDetalhadas.get(etapaReferenciada.task) : undefined;
1028
- const indiceOutput = taskReferenciada ? indicesCampos(taskReferenciada.output) : undefined;
1029
- if (etapaOrigem && campoSaida && etapaReferenciada && indiceOutput && !indiceOutput.has(campoSaida)) {
1030
- 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."));
1031
- }
1032
- }
1033
- }
1034
- }
1035
- }
1036
- if (item.etapa.condicao) {
1037
- const referencias = extrairReferenciasDaExpressao(item.etapa.condicao).map((referencia) => extrairRaiz(referencia));
1038
- for (const referencia of referencias) {
1039
- if (!ehMarcadorSemantico(referencia)
1040
- && !tasksConhecidas.has(referencia)
1041
- && !referenciaAgentConhecida(referencia, agentsConhecidos)
1042
- && !nomesEtapas.has(referencia)
1043
- && !contextoFlow.has(referencia)) {
1044
- diagnosticos.push(criarDiagnostico("SEM035", `Condicao da etapa "${item.etapa.nome}" em flow "${flow.nome}" referencia "${referencia}" fora do contexto atual.`, "erro", item.linha.intervalo, "Condicoes de flow devem apontar para marcadores semanticos, campos do flow, tasks, agents ou etapas anteriores."));
1045
- }
1046
- }
1047
- }
1048
- }
1049
- for (const item of etapas) {
1050
- if (!item.etapa) {
1051
- continue;
1052
- }
1053
- for (const dependencia of item.etapa.dependencias) {
1054
- if (!nomesEtapas.has(dependencia)) {
1055
- const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
1056
- 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."));
1057
- }
1058
- }
1059
- for (const destino of [item.etapa.emSucesso, item.etapa.emErro].filter(Boolean)) {
1060
- if (destino && !nomesEtapas.has(destino)) {
1061
- const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
1062
- 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."));
1063
- }
1064
- }
1065
- if (item.etapa.task && tasksConhecidas.has(item.etapa.task)) {
1066
- const detalhesTask = tarefasDetalhadas.get(item.etapa.task);
1067
- const indiceErrors = indiceErros(detalhesTask?.errors ?? []);
1068
- for (const rotaErro of item.etapa.porErro) {
1069
- if (!indiceErrors.has(rotaErro.tipo)) {
1070
- const sugestoesErros = descreverSugestoes(indiceErrors.keys(), "Erros declarados pela task");
1071
- 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."));
1072
- }
1073
- if (!nomesEtapas.has(rotaErro.destino)) {
1074
- const sugestoesEtapas = descreverSugestoes(nomesEtapas, "Etapas declaradas");
1075
- 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."));
1076
- }
1077
- }
1078
- }
1079
- }
1080
- }
1081
- function validarVinculoEstadoDaTask(task, statesConhecidos, diagnosticos) {
1082
- if (!task.state) {
1083
- return;
1084
- }
1085
- const nomeEstado = task.state.nome ?? task.state.campos.find((campo) => campo.nome === "state" || campo.nome === "estado")?.valor;
1086
- if (!nomeEstado) {
1087
- 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."));
1088
- return;
1089
- }
1090
- const estadoConhecido = statesConhecidos.get(nomeEstado);
1091
- if (!estadoConhecido) {
1092
- 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."));
1093
- return;
1094
- }
1095
- const blocoTransitions = localizarBloco(task.state, "transitions");
1096
- const linhasTransicao = blocoTransitions?.linhas ?? task.state.linhas;
1097
- if (linhasTransicao.length === 0) {
1098
- 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."));
1099
- return;
1100
- }
1101
- for (const linha of linhasTransicao) {
1102
- const transicao = parsearTransicaoEstado(linha.conteudo);
1103
- if (!transicao) {
1104
- 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."));
1105
- continue;
1106
- }
1107
- if (!estadoConhecido.transicoes.has(serializarTransicao(transicao.origem, transicao.destino))) {
1108
- 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."));
1109
- }
1110
- }
1111
- }
1112
- function validarContratoRoute(route, taskNome, tarefasDetalhadas, diagnosticos) {
1113
- const inputPublico = localizarBloco(route.corpo, "input");
1114
- const outputPublico = localizarBloco(route.corpo, "output");
1115
- const errorPublico = localizarBloco(route.corpo, "error");
1116
- const detalhesTask = tarefasDetalhadas.get(taskNome);
1117
- if (!detalhesTask) {
1118
- return;
1119
- }
1120
- const indiceInputTask = indicesCampos(detalhesTask.input);
1121
- const indiceOutputTask = indicesCampos(detalhesTask.output);
1122
- const indiceErrorsTask = indiceErros(detalhesTask.errors);
1123
- if (inputPublico) {
1124
- for (const campo of inputPublico.campos) {
1125
- const campoTask = indiceInputTask.get(campo.nome);
1126
- if (!campoTask) {
1127
- 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."));
1128
- continue;
1129
- }
1130
- if (campoTask.tipo !== campo.valor) {
1131
- 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."));
1132
- }
1133
- }
1134
- }
1135
- if (outputPublico) {
1136
- for (const campo of outputPublico.campos) {
1137
- const campoTask = indiceOutputTask.get(campo.nome);
1138
- if (!campoTask) {
1139
- 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."));
1140
- continue;
1141
- }
1142
- if (campoTask.tipo !== campo.valor) {
1143
- 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."));
1144
- }
1145
- }
1146
- }
1147
- if (errorPublico) {
1148
- for (const campo of errorPublico.campos) {
1149
- if (!indiceErrorsTask.has(campo.nome)) {
1150
- 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."));
1151
- }
1152
- }
1153
- }
1154
- }
1155
- function validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos) {
1156
- const metodo = localizarCampo(route.corpo, "metodo");
1157
- const caminho = localizarCampo(route.corpo, "caminho");
1158
- const caminhoResolvido = recomporCaminhoRoute(caminho);
1159
- const task = localizarCampo(route.corpo, "task", "tarefa");
1160
- const effects = localizarBloco(route.corpo, "effects");
1161
- if (!metodo) {
1162
- 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."));
1163
- }
1164
- if (!caminho) {
1165
- diagnosticos.push(criarDiagnostico("SEM015", `Route "${route.nome}" precisa declarar o campo caminho.`, "erro", route.intervalo, "Use um campo como caminho: \"/recurso\"."));
1166
- }
1167
- if (metodo) {
1168
- const metodosValidos = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
1169
- if (!metodosValidos.has(metodo.valor.toUpperCase())) {
1170
- 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."));
1171
- }
1172
- }
1173
- if (caminho && (!caminhoResolvido || !caminhoResolvido.startsWith("/"))) {
1174
- diagnosticos.push(criarDiagnostico("SEM017", `Route "${route.nome}" precisa usar um caminho iniciando com '/'.`, "erro", caminho.intervalo, "Exemplo valido: caminho: \"/produtos\"."));
1175
- }
1176
- if (effects) {
1177
- validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects da route ${route.nome}`);
1178
- }
1179
- validarVinculos(route.vinculos, diagnosticos, `route ${route.nome}`);
1180
- const perfilSeguranca = coletarPerfilSegurancaDeclarado(route.corpo, effects, diagnosticos, `route "${route.nome}"`);
1181
- emitirGuardrailsSeguranca(`Route "${route.nome}"`, route.intervalo, perfilSeguranca, diagnosticos, {
1182
- publico: extrairPerfilCompatibilidade(route.corpo, "publico") === "publico",
1183
- sensivel: routeEhMutante(route) || perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis,
1184
- });
1185
- if (task && !tasksConhecidas.has(task.valor)) {
1186
- 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."));
1187
- return;
1188
- }
1189
- if (task) {
1190
- validarContratoRoute(route, task.valor, tarefasDetalhadas, diagnosticos);
1191
- }
1192
- }
1193
- function validarGuardrailsSeguranca(modulo, diagnosticos) {
1194
- const superficies = coletarSuperficiesModulo(modulo);
1195
- for (const task of modulo.tasks) {
1196
- const motivos = new Set();
1197
- const rotasPublicasAssociadas = modulo.routes.filter((route) => localizarCampo(route.corpo, "task", "tarefa")?.valor === task.nome
1198
- && extrairPerfilCompatibilidade(route.corpo, "publico") === "publico");
1199
- const superficiesPublicasAssociadas = superficies.filter((item) => localizarCampo(item.superficie, "task", "tarefa")?.valor === task.nome
1200
- && superficieEhPublica(item.superficie, item.tipo));
1201
- const perfilTask = coletarPerfilSegurancaDeclarado(task.corpo, task.effects, undefined, `task "${task.nome}"`);
1202
- if (taskEhSensivel(task)) {
1203
- motivos.add("criticidade operacional alta/critica ou efeito sensivel");
1204
- }
1205
- for (const route of rotasPublicasAssociadas) {
1206
- motivos.add(`route publica "${route.nome}"`);
1207
- }
1208
- for (const item of superficiesPublicasAssociadas) {
1209
- motivos.add(`superficie publica ${item.tipo} "${item.superficie.nome ?? item.tipo}"`);
1210
- }
1211
- const motivosOrdenados = [...motivos];
1212
- if (motivosOrdenados.length === 0) {
1213
- continue;
1214
- }
1215
- const perfisPublicos = [
1216
- ...rotasPublicasAssociadas.map((route) => coletarPerfilSegurancaDeclarado(route.corpo, localizarBloco(route.corpo, "effects"), undefined, `route "${route.nome}"`)),
1217
- ...superficiesPublicasAssociadas.map((item) => coletarPerfilSegurancaDeclarado(item.superficie, localizarBloco(item.superficie, "effects"), undefined, `superficie ${item.tipo} "${item.superficie.nome ?? item.tipo}"`)),
1218
- ];
1219
- const perfilPublico = {
1220
- auth: { ...perfilTask.auth, explicita: perfilTask.auth.explicita || perfisPublicos.some((perfil) => perfil.auth.explicita) },
1221
- authz: {
1222
- ...perfilTask.authz,
1223
- explicita: perfilTask.authz.explicita || perfisPublicos.some((perfil) => perfil.authz.explicita),
1224
- papeis: [...new Set([perfilTask.authz.papeis, ...perfisPublicos.map((perfil) => perfil.authz.papeis)].flat())],
1225
- escopos: [...new Set([perfilTask.authz.escopos, ...perfisPublicos.map((perfil) => perfil.authz.escopos)].flat())],
1226
- },
1227
- dados: {
1228
- ...perfilTask.dados,
1229
- explicita: perfilTask.dados.explicita || perfisPublicos.some((perfil) => perfil.dados.explicita),
1230
- campos: [...perfilTask.dados.campos, ...perfisPublicos.flatMap((perfil) => perfil.dados.campos)],
1231
- },
1232
- audit: { ...perfilTask.audit, explicita: perfilTask.audit.explicita || perfisPublicos.some((perfil) => perfil.audit.explicita) },
1233
- segredos: {
1234
- ...perfilTask.segredos,
1235
- explicita: perfilTask.segredos.explicita || perfisPublicos.some((perfil) => perfil.segredos.explicita),
1236
- itens: [...perfilTask.segredos.itens, ...perfisPublicos.flatMap((perfil) => perfil.segredos.itens)],
1237
- },
1238
- forbidden: {
1239
- ...perfilTask.forbidden,
1240
- explicita: perfilTask.forbidden.explicita || perfisPublicos.some((perfil) => perfil.forbidden.explicita),
1241
- regras: [...new Set([perfilTask.forbidden.regras, ...perfisPublicos.map((perfil) => perfil.forbidden.regras)].flat())],
1242
- },
1243
- efeitoPrivilegiado: perfilTask.efeitoPrivilegiado || perfisPublicos.some((perfil) => perfil.efeitoPrivilegiado),
1244
- dadosSensiveis: perfilTask.dadosSensiveis || perfisPublicos.some((perfil) => perfil.dadosSensiveis),
1245
- exigeSegredos: perfilTask.exigeSegredos || perfisPublicos.some((perfil) => perfil.exigeSegredos),
1246
- };
1247
- if (rotasPublicasAssociadas.length > 0 || superficiesPublicasAssociadas.length > 0) {
1248
- emitirGuardrailsSeguranca(`Task "${task.nome}" exposta publicamente`, task.intervalo, perfilPublico, diagnosticos, { publico: true, sensivel: false });
1249
- }
1250
- const resumoMotivos = motivosOrdenados.join(", ");
1251
- if (!task.execucao) {
1252
- diagnosticos.push(criarDiagnostico("SEM071", `Task "${task.nome}" exige execucao explicita para producao por causa de ${resumoMotivos}, mas ainda opera com execucao implicita.`, "aviso", task.intervalo, "Declare timeout, retry, compensacao, idempotencia e criticidade_operacional no bloco execucao da task."));
1253
- }
1254
- if (!taskTemRastreabilidade(task)) {
1255
- diagnosticos.push(criarDiagnostico("SEM072", `Task "${task.nome}" exige rastreabilidade forte por causa de ${resumoMotivos}, mas ainda nao declara impl nem vinculos.`, "aviso", task.intervalo, "Adicione impl e/ou vinculos para apontar arquivo, simbolo, recurso ou superficie real do codigo vivo."));
1256
- }
1257
- }
1258
- for (const item of superficies) {
1259
- if (!superficieEhPublica(item.superficie, item.tipo)) {
1260
- continue;
1261
- }
1262
- const execucao = localizarBloco(item.superficie, "execucao");
1263
- if (!execucao) {
1264
- const nomeSuperficie = item.superficie.nome ?? item.tipo;
1265
- diagnosticos.push(criarDiagnostico("SEM073", `Superficie publica ${item.tipo} "${nomeSuperficie}" deveria declarar execucao explicita para producao, mas ainda depende do padrao implicito.`, "aviso", item.superficie.intervalo, `Declare timeout, retry, compensacao e criticidade_operacional no proprio bloco ${item.tipo}.`));
1266
- }
1267
- }
1268
- }
1269
- function valoresDeclarativos(bloco) {
1270
- if (!bloco) {
1271
- return [];
1272
- }
1273
- return [
1274
- ...bloco.linhas.map((linha) => linha.conteudo),
1275
- ...bloco.campos.map((campo) => valorCampoCompleto(campo) ?? campo.nome),
1276
- ].filter((valor) => valor.trim().length > 0);
1277
- }
1278
- function valoresDeclarativosProfundos(bloco) {
1279
- if (!bloco) {
1280
- return [];
1281
- }
1282
- return [
1283
- nomeBlocoDeclarativo(bloco),
1284
- bloco.nome ?? "",
1285
- ...valoresDeclarativos(bloco),
1286
- ...bloco.campos.flatMap((campo) => [campo.nome, campo.valor, ...campo.modificadores]),
1287
- ...bloco.blocos.flatMap((filho) => filho.tipo === "bloco_generico" ? valoresDeclarativosProfundos(filho) : []),
1288
- ].filter((valor) => valor.trim().length > 0);
1289
- }
1290
- function normalizarTextoAuthorSensivel(valor) {
1291
- return valor
1292
- .normalize("NFD")
1293
- .replace(/[\u0300-\u036f]/g, "")
1294
- .toLowerCase()
1295
- .replace(/[_\W]+/g, " ")
1296
- .replace(/\s+/g, " ")
1297
- .trim();
1298
- }
1299
- function textoContemTermoAuthorSensivel(valor) {
1300
- const texto = ` ${normalizarTextoAuthorSensivel(valor)} `;
1301
- return TERMOS_AUTHOR_SENSIVEIS.some((termo) => {
1302
- const termoNormalizado = normalizarTextoAuthorSensivel(termo);
1303
- if (termoNormalizado.length <= 3) {
1304
- return texto.includes(` ${termoNormalizado} `);
1305
- }
1306
- return texto.includes(termoNormalizado);
1307
- });
1308
- }
1309
- function blocoAuthorMarcaSensivel(bloco) {
1310
- const campoSensivel = localizarCampo(bloco, "sensivel", "sensitive", "tema_sensivel");
1311
- const valorSensivel = normalizarTextoAuthorSensivel(valorCampoCompleto(campoSensivel) ?? "");
1312
- if (["verdadeiro", "true", "sim", "alto", "alta", "critico", "critica"].includes(valorSensivel)) {
1313
- return true;
1314
- }
1315
- return valoresDeclarativosProfundos(bloco).some(textoContemTermoAuthorSensivel);
1316
- }
1317
- function coletarAuthorBlocksPorTipo(modulo, tipos) {
1318
- return modulo.authorBlocks.filter((bloco) => tipos.includes(nomeBlocoDeclarativo(bloco)));
1319
- }
1320
- function coletarSubblocosPorNomes(bloco, nomes) {
1321
- return bloco.blocos.filter((item) => item.tipo === "bloco_generico" && blocoTemNome(item, nomes));
1322
- }
1323
- function validarSubblocoNaoVazio(bloco, diagnosticos, codigo, contexto) {
1324
- if (bloco.campos.length > 0 || bloco.linhas.length > 0 || bloco.blocos.length > 0) {
1325
- return;
1326
- }
1327
- diagnosticos.push(criarDiagnostico(codigo, `${contexto} declarou "${nomeBlocoDeclarativo(bloco)}" vazio.`, "erro", bloco.intervalo, "Declare ao menos uma linha ou campo para que o guardrail seja verificavel por IA."));
1328
- }
1329
- function validarAuthorBlock(bloco, diagnosticos) {
1330
- const tipo = nomeBlocoDeclarativo(bloco);
1331
- const contexto = `${tipo} "${bloco.nome ?? tipo}"`;
1332
- if (["book", "work"].includes(tipo) && !localizarCampo(bloco, "titulo", "title", "proposito", "purpose")) {
1333
- diagnosticos.push(criarDiagnostico("SEM120", `${contexto} deveria declarar titulo ou proposito para ancorar a obra autoral.`, "aviso", bloco.intervalo, "Use titulo/title para nome editorial ou proposito/purpose para livro tecnico, ensaio, manual ou obra nao ficcional."));
1334
- }
1335
- if (["chapter", "section"].includes(tipo) && !localizarCampo(bloco, "intent", "intencao", "objetivo", "purpose")) {
1336
- diagnosticos.push(criarDiagnostico("SEM121", `${contexto} deveria declarar intencao ou objetivo.`, "aviso", bloco.intervalo, "Declare intent/intencao/objetivo para que a IA nao preencha a secao com texto generico."));
1337
- }
1338
- if (tipo === "scene" && !localizarCampo(bloco, "pov", "ponto_de_vista")) {
1339
- diagnosticos.push(criarDiagnostico("SEM122", `${contexto} deveria declarar pov para preservar continuidade narrativa.`, "aviso", bloco.intervalo, "Use pov ou ponto_de_vista quando a obra tiver cenas narrativas."));
1340
- }
1341
- if (tipo === "voice" && !localizarCampo(bloco, "tom", "tone", "registro", "ritmo")) {
1342
- diagnosticos.push(criarDiagnostico("SEM123", `${contexto} deveria declarar tom, registro ou ritmo.`, "aviso", bloco.intervalo, "Declare a voz da obra para reduzir texto raso, repetitivo ou com cara de IA."));
1343
- }
1344
- if (tipo === "claim" && !localizarCampo(bloco, "source", "fonte", "evidencia", "confidence", "confianca")) {
1345
- diagnosticos.push(criarDiagnostico("SEM124", `${contexto} deveria declarar fonte, evidencia ou confianca.`, "aviso", bloco.intervalo, "Claims de nao-ficcao precisam de rastreio para diferenciar argumento, opiniao e evidencia."));
1346
- }
1347
- if (tipo === "style_rule") {
1348
- const guardrails = coletarSubblocosPorNomes(bloco, ["forbidden", "proibido", "avoid", "evitar", "echo", "ecos"]);
1349
- const tolerancia = localizarBlocoPorNomes(bloco, "tolerancia", "tolerance");
1350
- if (guardrails.length === 0 && !tolerancia && bloco.campos.length === 0 && bloco.linhas.length === 0) {
1351
- diagnosticos.push(criarDiagnostico("SEM125", `${contexto} precisa declarar proibicoes, ecos, tolerancia ou preferencias.`, "erro", bloco.intervalo, "Use proibido/forbidden para cliches, echo/ecos para repeticao, evitar/avoid para muletas ou tolerancia para limites."));
1352
- }
1353
- for (const guardrail of guardrails) {
1354
- validarSubblocoNaoVazio(guardrail, diagnosticos, "SEM126", contexto);
1355
- }
1356
- const vistos = new Set();
1357
- for (const guardrail of guardrails) {
1358
- for (const valor of valoresDeclarativos(guardrail)) {
1359
- const chave = valor.toLowerCase();
1360
- if (vistos.has(chave)) {
1361
- diagnosticos.push(criarDiagnostico("SEM127", `${contexto} repetiu o guardrail de estilo "${valor}".`, "aviso", guardrail.intervalo, "Remova duplicatas para manter a regra de estilo enxuta."));
1362
- }
1363
- vistos.add(chave);
1364
- }
1365
- }
1366
- }
1367
- }
1368
- function styleRuleTemProibicoes(bloco) {
1369
- return coletarSubblocosPorNomes(bloco, ["forbidden", "proibido", "avoid", "evitar"]).some((guardrail) => valoresDeclarativos(guardrail).length > 0);
1370
- }
1371
- function flowReferenciaAgent(flow, agentsConhecidos) {
1372
- for (const linha of flow.corpo.linhas) {
1373
- const etapa = parsearEtapaFlow(linha.conteudo);
1374
- if (etapa?.task && referenciaAgentConhecida(etapa.task, agentsConhecidos)) {
1375
- return true;
1376
- }
1377
- }
1378
- const task = localizarCampo(flow.corpo, "task")?.valor;
1379
- return Boolean(task && referenciaAgentConhecida(task, agentsConhecidos));
1380
- }
1381
- function validarAuthorSensivel(modulo, agentsConhecidos, diagnosticos) {
1382
- if (modulo.authorBlocks.length === 0 || !modulo.authorBlocks.some(blocoAuthorMarcaSensivel)) {
1383
- return;
1384
- }
1385
- const obras = coletarAuthorBlocksPorTipo(modulo, ["book", "work"]);
1386
- const audiences = coletarAuthorBlocksPorTipo(modulo, ["audience"]);
1387
- const claims = coletarAuthorBlocksPorTipo(modulo, ["claim"]);
1388
- const sources = coletarAuthorBlocksPorTipo(modulo, ["source"]);
1389
- const styleRules = coletarAuthorBlocksPorTipo(modulo, ["style_rule"]);
1390
- const primeiroBlocoSensivel = modulo.authorBlocks.find(blocoAuthorMarcaSensivel) ?? modulo.authorBlocks[0];
1391
- const intervaloBase = primeiroBlocoSensivel?.intervalo;
1392
- if (obras.length === 0) {
1393
- diagnosticos.push(criarDiagnostico("SEM132", "Author sensivel precisa declarar book/work para ancorar proposito, tipo, publico e limites da obra.", "erro", intervaloBase, "Declare book ou work antes de pedir para a IA criar ou editar material sensivel."));
1394
- }
1395
- if (audiences.length === 0) {
1396
- diagnosticos.push(criarDiagnostico("SEM133", "Author sensivel precisa declarar audience/publico antes da IA escrever ou editar.", "erro", intervaloBase, "Declare se o livro fala com pais, educadores, pessoas autistas, profissionais, publico geral ou outro grupo."));
1397
- }
1398
- if (claims.length === 0) {
1399
- diagnosticos.push(criarDiagnostico("SEM134", "Author sensivel precisa declarar claim para separar fato, argumento, opiniao e promessa editorial.", "erro", intervaloBase, "Declare ao menos um claim para qualquer tese factual que a IA deva sustentar ou revisar."));
1400
- }
1401
- if (sources.length === 0) {
1402
- diagnosticos.push(criarDiagnostico("SEM135", "Author sensivel precisa declarar source/fonte rastreavel.", "erro", intervaloBase, "Declare source para DSM, CID, artigo, livro, guia clinico, entrevista, experiencia autoral ou outra fonte aceita."));
1403
- }
1404
- if (styleRules.length === 0) {
1405
- diagnosticos.push(criarDiagnostico("SEM136", "Author sensivel precisa declarar style_rule para bloquear cliche, infantilizacao, generalizacao e linguagem ruim.", "erro", intervaloBase, "Declare style_rule com proibido/evitar/ecos/tolerancia antes da IA gerar texto."));
1406
- }
1407
- if (modulo.agents.length === 0) {
1408
- diagnosticos.push(criarDiagnostico("SEM137", "Author sensivel precisa declarar agent governado para revisao, fonte, sensibilidade ou estilo.", "erro", intervaloBase, "Declare ao menos um agent com role, goal, tools, memory e policy."));
1409
- }
1410
- if (!modulo.flows.some((flow) => flowReferenciaAgent(flow, agentsConhecidos))) {
1411
- diagnosticos.push(criarDiagnostico("SEM138", "Author sensivel precisa declarar flow chamando agent governado.", "erro", intervaloBase, "Modele etapas como escrever, checar fonte, revisar linguagem, validar sensibilidade e preservar canon."));
1412
- }
1413
- for (const claim of claims) {
1414
- if (!localizarCampo(claim, "source", "fonte", "evidencia")) {
1415
- diagnosticos.push(criarDiagnostico("SEM139", `Claim sensivel "${claim.nome ?? "claim"}" precisa declarar source/fonte/evidencia.`, "erro", claim.intervalo, "Toda afirmacao factual sensivel precisa apontar fonte ou evidencia antes da IA usar no texto."));
1416
- }
1417
- if (!localizarCampo(claim, "confidence", "confianca")) {
1418
- diagnosticos.push(criarDiagnostico("SEM140", `Claim sensivel "${claim.nome ?? "claim"}" precisa declarar confidence/confianca.`, "erro", claim.intervalo, "Declare o nivel de confianca para a IA diferenciar fato forte, consenso limitado e hipotese."));
1419
- }
1420
- }
1421
- for (const styleRule of styleRules) {
1422
- if (!styleRuleTemProibicoes(styleRule)) {
1423
- diagnosticos.push(criarDiagnostico("SEM141", `Style_rule sensivel "${styleRule.nome ?? "style_rule"}" precisa declarar proibido/evitar verificavel.`, "erro", styleRule.intervalo, "Inclua termos, frases ou abordagens proibidas, como infantilizacao, generalizacao, promessa clinica ou cliche."));
1424
- }
1425
- }
1426
- }
1427
- function validarAgent(agent, diagnosticos) {
1428
- const nome = agent.nome ?? "agent";
1429
- if (!localizarCampo(agent, "role", "papel") && !localizarCampo(agent, "goal", "objetivo")) {
1430
- diagnosticos.push(criarDiagnostico("SEM128", `Agent "${nome}" precisa declarar role/papel ou goal/objetivo.`, "erro", agent.intervalo, "Agente sem objetivo vira prompt solto. Declare role ou goal."));
1431
- }
1432
- const tools = localizarBlocoPorNomes(agent, "tools", "ferramentas");
1433
- if (!tools || valoresDeclarativos(tools).length === 0) {
1434
- diagnosticos.push(criarDiagnostico("SEM129", `Agent "${nome}" precisa declarar tools/ferramentas.`, "erro", agent.intervalo, "Declare as ferramentas que o agente pode usar para limitar acao e dar rastreabilidade."));
1435
- }
1436
- const policy = localizarBlocoPorNomes(agent, "policy", "politica");
1437
- if (!policy || valoresDeclarativos(policy).length === 0) {
1438
- diagnosticos.push(criarDiagnostico("SEM130", `Agent "${nome}" precisa declarar policy/politica.`, "erro", agent.intervalo, "Declare limites como nao_reescrever_sem_aprovacao, citar_trechos_afetados ou atualizar_docs_apos_edicao."));
1439
- }
1440
- const memory = localizarBlocoPorNomes(agent, "memory", "memoria");
1441
- if (!memory || valoresDeclarativos(memory).length === 0) {
1442
- diagnosticos.push(criarDiagnostico("SEM131", `Agent "${nome}" deveria declarar memory/memoria para evitar contexto improvisado.`, "aviso", agent.intervalo, "Declare quais memorias, docs ou contratos o agente deve consultar antes de agir."));
1443
- }
1444
- }
1445
- export function criarContextoLocal(modulo) {
1446
- const simbolos = new Map();
1447
- const tiposConhecidos = new Set(TIPOS_PRIMITIVOS);
1448
- const tasksConhecidas = new Set();
1449
- const agentsConhecidos = new Set();
1450
- const tarefasDetalhadas = new Map();
1451
- const statesConhecidos = new Map();
1452
- const enumsConhecidos = new Map();
1453
- const registrar = (nome, categoria) => {
1454
- if (simbolos.has(nome)) {
1455
- return;
1456
- }
1457
- simbolos.set(nome, { nome, categoria });
1458
- if (categoria === "task") {
1459
- tasksConhecidas.add(nome);
1460
- return;
1461
- }
1462
- if (categoria === "agent") {
1463
- agentsConhecidos.add(nome);
1464
- return;
1465
- }
1466
- if (categoria !== "database") {
1467
- tiposConhecidos.add(nome);
1468
- }
1469
- };
1470
- for (const type of modulo.types) {
1471
- registrar(type.nome, "tipo");
1472
- }
1473
- for (const entity of modulo.entities) {
1474
- registrar(entity.nome, "entity");
1475
- }
1476
- for (const enumeracao of modulo.enums) {
1477
- registrar(enumeracao.nome, "enum");
1478
- enumsConhecidos.set(enumeracao.nome, new Set(enumeracao.valores));
1479
- }
1480
- for (const task of modulo.tasks) {
1481
- registrar(task.nome, "task");
1482
- tarefasDetalhadas.set(task.nome, coletarResumoTask(task));
1483
- }
1484
- for (const flow of modulo.flows) {
1485
- registrar(flow.nome, "flow");
1486
- }
1487
- for (const route of modulo.routes) {
1488
- registrar(route.nome, "route");
1489
- }
1490
- for (const authorBlock of modulo.authorBlocks) {
1491
- if (authorBlock.nome && TIPOS_AUTHOR_PROFILE.has(authorBlock.palavraChave)) {
1492
- registrar(authorBlock.nome, authorBlock.palavraChave);
1493
- }
1494
- }
1495
- for (const agent of modulo.agents) {
1496
- if (agent.nome) {
1497
- registrar(agent.nome, "agent");
1498
- }
1499
- }
1500
- for (const worker of modulo.workers) {
1501
- if (worker.nome) {
1502
- registrar(worker.nome, "worker");
1503
- }
1504
- }
1505
- for (const evento of modulo.eventos) {
1506
- if (evento.nome) {
1507
- registrar(evento.nome, "evento");
1508
- }
1509
- }
1510
- for (const fila of modulo.filas) {
1511
- if (fila.nome) {
1512
- registrar(fila.nome, "fila");
1513
- }
1514
- }
1515
- for (const cron of modulo.crons) {
1516
- if (cron.nome) {
1517
- registrar(cron.nome, "cron");
1518
- }
1519
- }
1520
- for (const webhook of modulo.webhooks) {
1521
- if (webhook.nome) {
1522
- registrar(webhook.nome, "webhook");
1523
- }
1524
- }
1525
- for (const cache of modulo.caches) {
1526
- if (cache.nome) {
1527
- registrar(cache.nome, "cache");
1528
- }
1529
- }
1530
- for (const storage of modulo.storages) {
1531
- if (storage.nome) {
1532
- registrar(storage.nome, "storage");
1533
- }
1534
- }
1535
- for (const policy of modulo.policies) {
1536
- if (policy.nome) {
1537
- registrar(policy.nome, "policy");
1538
- }
1539
- }
1540
- for (const database of modulo.databases) {
1541
- if (database.nome) {
1542
- registrar(database.nome, "database");
1543
- }
1544
- }
1545
- for (const state of modulo.states) {
1546
- if (state.nome) {
1547
- registrar(state.nome, "state");
1548
- const transicoes = new Set((localizarBloco(state.corpo, "transitions")?.linhas ?? [])
1549
- .map((linha) => parsearTransicaoEstado(linha.conteudo))
1550
- .filter((linha) => Boolean(linha))
1551
- .map((linha) => serializarTransicao(linha.origem, linha.destino)));
1552
- statesConhecidos.set(state.nome, { transicoes });
1553
- }
1554
- }
1555
- return {
1556
- modulo: modulo.nome,
1557
- simbolos,
1558
- tiposConhecidos,
1559
- tasksConhecidas,
1560
- agentsConhecidos,
1561
- tarefasDetalhadas,
1562
- statesConhecidos,
1563
- modulosImportados: [],
1564
- interoperabilidades: modulo.uses
1565
- .filter(ehUseInterop)
1566
- .map((use) => ({ origem: use.origem, caminho: use.caminho })),
1567
- enumsConhecidos,
1568
- };
1569
- }
1570
- function validarTask(task, tiposConhecidos, statesConhecidos, diagnosticos) {
1571
- if (!task.input) {
1572
- diagnosticos.push(criarDiagnostico("SEM003", `Task "${task.nome}" precisa declarar input.`, "erro", task.intervalo, "Toda task precisa declarar as entradas de forma explicita."));
1573
- }
1574
- if (!task.output) {
1575
- diagnosticos.push(criarDiagnostico("SEM004", `Task "${task.nome}" precisa declarar output.`, "erro", task.intervalo, "Toda task precisa declarar a saida esperada."));
1576
- }
1577
- if (!task.guarantees) {
1578
- 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."));
1579
- }
1580
- if (task.input) {
1581
- validarCamposDeTipos(task.input.campos, tiposConhecidos, diagnosticos, `input da task ${task.nome}`);
1582
- }
1583
- if (task.output) {
1584
- validarCamposDeTipos(task.output.campos, tiposConhecidos, diagnosticos, `output da task ${task.nome}`);
1585
- }
1586
- const entradasConhecidas = new Set(task.input?.campos.map((campo) => campo.nome) ?? []);
1587
- const saidasConhecidas = new Set(task.output?.campos.map((campo) => campo.nome) ?? []);
1588
- if (task.rules) {
1589
- validarExpressoesDeclaradas(task.rules.linhas, diagnosticos, {
1590
- codigoErroSintaxe: "SEM021",
1591
- codigoErroReferencia: "SEM022",
1592
- nomeBloco: `rules da task ${task.nome}`,
1593
- simbolosPermitidos: entradasConhecidas,
1594
- dicaSintaxe: "Use expressoes como \"campo existe\", \"campo > 0\", \"campo em [A, B]\" ou \"campo deve_ser predicado\".",
1595
- dicaReferencia: "No MVP atual, rules devem referenciar apenas campos do input.",
1596
- dicaReferenciaPersonalizada: (raiz) => (saidasConhecidas.has(raiz)
1597
- ? `\"${raiz}\" parece vir do output. Rules devem validar entrada; se a intencao era afirmar pos-condicao, mova isso para guarantees.`
1598
- : undefined),
1599
- });
1600
- }
1601
- if (task.effects) {
1602
- validarEfeitosDeclarados(task.effects.linhas, diagnosticos, `effects da task ${task.nome}`);
1603
- }
1604
- validarVinculos(task.vinculos, diagnosticos, `task ${task.nome}`);
1605
- validarExecucao(task, diagnosticos);
1606
- const perfilSeguranca = coletarPerfilSegurancaDeclarado(task.corpo, task.effects, diagnosticos, `task "${task.nome}"`);
1607
- emitirGuardrailsSeguranca(`Task "${task.nome}"`, task.intervalo, perfilSeguranca, diagnosticos, {
1608
- publico: false,
1609
- sensivel: taskEhSensivel(task) || perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis || perfilSeguranca.exigeSegredos,
1610
- });
1611
- validarImplementacoesTask(task, diagnosticos);
1612
- if (task.tests) {
1613
- for (const bloco of task.tests.blocos) {
1614
- if (bloco.tipo !== "caso_teste") {
1615
- continue;
1616
- }
1617
- validarCasoTeste(task, bloco, diagnosticos);
1618
- }
1619
- }
1620
- if (task.guarantees && task.output) {
1621
- validarExpressoesDeclaradas(task.guarantees.linhas, diagnosticos, {
1622
- codigoErroSintaxe: "SEM030",
1623
- codigoErroReferencia: "SEM031",
1624
- nomeBloco: `guarantees da task ${task.nome}`,
1625
- simbolosPermitidos: saidasConhecidas,
1626
- dicaSintaxe: "Use expressoes como \"saida existe\", \"saida == valor\" ou \"saida em [A, B]\" nas guarantees.",
1627
- dicaReferencia: "No MVP atual, guarantees devem referenciar campos do output ou marcadores semanticos permitidos.",
1628
- aceitarMarcadoresSemanticos: true,
1629
- dicaReferenciaPersonalizada: (raiz) => (entradasConhecidas.has(raiz)
1630
- ? `\"${raiz}\" parece vir do input. Guarantees devem afirmar output, estado ou marcadores semanticos; se a intencao era validar entrada, mova isso para rules.`
1631
- : undefined),
1632
- });
1633
- }
1634
- validarErroOperacional(task, diagnosticos);
1635
- const blocoInternoTests = localizarBloco(task.corpo, "tests");
1636
- if (blocoInternoTests && blocoInternoTests.blocos.length === 0) {
1637
- diagnosticos.push(criarDiagnostico("SEM007", `Task "${task.nome}" declarou tests sem casos.`, "erro", blocoInternoTests.intervalo, "Adicione ao menos um bloco caso dentro de tests."));
1638
- }
1639
- validarVinculoEstadoDaTask(task, statesConhecidos, diagnosticos);
1640
- }
1641
- function validarCasoTeste(task, caso, diagnosticos) {
1642
- if (!caso.given) {
1643
- diagnosticos.push(criarDiagnostico("SEM008", `Caso de teste "${caso.nome}" da task "${task.nome}" precisa declarar given.`, "erro", caso.intervalo));
1644
- }
1645
- if (!caso.expect) {
1646
- diagnosticos.push(criarDiagnostico("SEM009", `Caso de teste "${caso.nome}" da task "${task.nome}" precisa declarar expect.`, "erro", caso.intervalo));
1647
- }
1648
- }
1649
- export function analisarSemantica(modulo, opcoes = {}) {
1650
- const diagnosticos = [];
1651
- const simbolos = new Map();
1652
- const tiposConhecidos = new Set(TIPOS_PRIMITIVOS);
1653
- const tasksConhecidas = new Set();
1654
- const agentsConhecidos = new Set();
1655
- const tarefasDetalhadas = new Map();
1656
- const statesConhecidos = new Map();
1657
- const modulosImportados = [];
1658
- const interoperabilidades = [];
1659
- const enumsConhecidos = new Map();
1660
- for (const use of modulo.uses) {
1661
- if (use.origem !== "sema") {
1662
- if (!PADRAO_CAMINHO_INTEROP.test(use.caminho)) {
1663
- 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."));
1664
- continue;
1665
- }
1666
- interoperabilidades.push({ origem: use.origem, caminho: use.caminho });
1667
- continue;
1668
- }
1669
- const resolucaoUse = resolverUseSema(modulo.nome, use.caminho, opcoes.contextosModulos);
1670
- const contextoImportado = resolucaoUse.caminhoResolvido
1671
- ? opcoes.contextosModulos?.get(resolucaoUse.caminhoResolvido)
1672
- : undefined;
1673
- if (!contextoImportado) {
1674
- const candidatosEncontrados = resolucaoUse.candidatosRelativos
1675
- .filter((candidato) => opcoes.contextosModulos?.has(candidato))
1676
- .slice(0, 4);
1677
- const sugestoesModulos = descreverSugestoes(opcoes.contextosModulos?.keys() ?? [], "Modulos disponiveis neste contexto");
1678
- 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
1679
- ? `Se a intencao era um import relativo ao namespace atual, tente um caminho como ${candidatosEncontrados.join(", ")}.`
1680
- : (sugestoesModulos ?? "Garanta que o arquivo .sema importado esteja presente no mesmo conjunto de compilacao.")));
1681
- continue;
1682
- }
1683
- modulosImportados.push(resolucaoUse.caminhoResolvido ?? use.caminho);
1684
- for (const tipo of contextoImportado.tiposConhecidos) {
1685
- tiposConhecidos.add(tipo);
1686
- }
1687
- for (const task of contextoImportado.tasksConhecidas) {
1688
- tasksConhecidas.add(task);
1689
- }
1690
- for (const agent of contextoImportado.agentsConhecidos) {
1691
- agentsConhecidos.add(agent);
1692
- }
1693
- for (const [nomeTask, detalhesTask] of contextoImportado.tarefasDetalhadas) {
1694
- tarefasDetalhadas.set(nomeTask, {
1695
- input: detalhesTask.input.map((campo) => ({ ...campo, modificadores: [...campo.modificadores] })),
1696
- output: detalhesTask.output.map((campo) => ({ ...campo, modificadores: [...campo.modificadores] })),
1697
- errors: detalhesTask.errors.map((erro) => ({ ...erro })),
1698
- guarantees: [...detalhesTask.guarantees],
1699
- implementacoes: detalhesTask.implementacoes.map((impl) => ({ ...impl })),
1700
- });
1701
- }
1702
- for (const [nomeState, metadadosState] of contextoImportado.statesConhecidos) {
1703
- statesConhecidos.set(nomeState, { transicoes: new Set(metadadosState.transicoes) });
1704
- }
1705
- for (const [nomeEnum, valores] of contextoImportado.enumsConhecidos) {
1706
- enumsConhecidos.set(nomeEnum, new Set(valores));
1707
- }
1708
- for (const interop of contextoImportado.interoperabilidades) {
1709
- interoperabilidades.push({ ...interop });
1710
- }
1711
- }
1712
- const registrar = (nome, categoria, intervalo) => {
1713
- if (simbolos.has(nome)) {
1714
- diagnosticos.push(diagnosticoDuplicado(nome, categoria, intervalo));
1715
- return;
1716
- }
1717
- simbolos.set(nome, { nome, categoria });
1718
- if (categoria === "task") {
1719
- tasksConhecidas.add(nome);
1720
- return;
1721
- }
1722
- if (categoria === "agent") {
1723
- agentsConhecidos.add(nome);
1724
- return;
1725
- }
1726
- if (categoria !== "database") {
1727
- tiposConhecidos.add(nome);
1728
- }
1729
- };
1730
- for (const type of modulo.types) {
1731
- registrar(type.nome, "tipo", type.intervalo);
1732
- }
1733
- for (const entity of modulo.entities) {
1734
- registrar(entity.nome, "entity", entity.intervalo);
1735
- }
1736
- for (const enumeracao of modulo.enums) {
1737
- registrar(enumeracao.nome, "enum", enumeracao.intervalo);
1738
- enumsConhecidos.set(enumeracao.nome, new Set(enumeracao.valores));
1739
- }
1740
- for (const task of modulo.tasks) {
1741
- registrar(task.nome, "task", task.intervalo);
1742
- tarefasDetalhadas.set(task.nome, coletarResumoTask(task));
1743
- }
1744
- for (const flow of modulo.flows) {
1745
- registrar(flow.nome, "flow", flow.intervalo);
1746
- }
1747
- for (const route of modulo.routes) {
1748
- registrar(route.nome, "route", route.intervalo);
1749
- }
1750
- for (const authorBlock of modulo.authorBlocks) {
1751
- if (authorBlock.nome && TIPOS_AUTHOR_PROFILE.has(authorBlock.palavraChave)) {
1752
- registrar(authorBlock.nome, authorBlock.palavraChave, authorBlock.intervalo);
1753
- }
1754
- }
1755
- for (const agent of modulo.agents) {
1756
- if (agent.nome) {
1757
- registrar(agent.nome, "agent", agent.intervalo);
1758
- }
1759
- }
1760
- for (const worker of modulo.workers) {
1761
- if (worker.nome) {
1762
- registrar(worker.nome, "worker", worker.intervalo);
1763
- }
1764
- }
1765
- for (const evento of modulo.eventos) {
1766
- if (evento.nome) {
1767
- registrar(evento.nome, "evento", evento.intervalo);
1768
- }
1769
- }
1770
- for (const fila of modulo.filas) {
1771
- if (fila.nome) {
1772
- registrar(fila.nome, "fila", fila.intervalo);
1773
- }
1774
- }
1775
- for (const cron of modulo.crons) {
1776
- if (cron.nome) {
1777
- registrar(cron.nome, "cron", cron.intervalo);
1778
- }
1779
- }
1780
- for (const webhook of modulo.webhooks) {
1781
- if (webhook.nome) {
1782
- registrar(webhook.nome, "webhook", webhook.intervalo);
1783
- }
1784
- }
1785
- for (const cache of modulo.caches) {
1786
- if (cache.nome) {
1787
- registrar(cache.nome, "cache", cache.intervalo);
1788
- }
1789
- }
1790
- for (const storage of modulo.storages) {
1791
- if (storage.nome) {
1792
- registrar(storage.nome, "storage", storage.intervalo);
1793
- }
1794
- }
1795
- for (const policy of modulo.policies) {
1796
- if (policy.nome) {
1797
- registrar(policy.nome, "policy", policy.intervalo);
1798
- }
1799
- }
1800
- for (const database of modulo.databases) {
1801
- if (database.nome) {
1802
- registrar(database.nome, "database", database.intervalo);
1803
- }
1804
- }
1805
- for (const state of modulo.states) {
1806
- if (state.nome) {
1807
- registrar(state.nome, "state", state.intervalo);
1808
- const transicoes = new Set((localizarBloco(state.corpo, "transitions")?.linhas ?? [])
1809
- .map((linha) => parsearTransicaoEstado(linha.conteudo))
1810
- .filter((linha) => Boolean(linha))
1811
- .map((linha) => serializarTransicao(linha.origem, linha.destino)));
1812
- statesConhecidos.set(state.nome, { transicoes });
1813
- }
1814
- }
1815
- for (const type of modulo.types) {
1816
- validarCamposDeTipos(type.corpo.campos, tiposConhecidos, diagnosticos, `type ${type.nome}`);
1817
- const fields = localizarBloco(type.corpo, "fields");
1818
- if (fields) {
1819
- validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `fields do type ${type.nome}`);
1820
- }
1821
- validarInvariantesDeCampos(type.corpo, `type ${type.nome}`, diagnosticos);
1822
- }
1823
- for (const entity of modulo.entities) {
1824
- const fields = localizarBloco(entity.corpo, "fields");
1825
- if (!fields || fields.campos.length === 0) {
1826
- diagnosticos.push(criarDiagnostico("SEM010", `Entity "${entity.nome}" precisa declarar fields.`, "erro", entity.intervalo, "Adicione um bloco fields com os campos da entidade."));
1827
- }
1828
- else {
1829
- validarCamposDeTipos(fields.campos, tiposConhecidos, diagnosticos, `entity ${entity.nome}`);
1830
- }
1831
- validarInvariantesDeCampos(entity.corpo, `entity ${entity.nome}`, diagnosticos);
1832
- }
1833
- for (const task of modulo.tasks) {
1834
- validarTask(task, tiposConhecidos, statesConhecidos, diagnosticos);
1835
- }
1836
- for (const flow of modulo.flows) {
1837
- validarFlow(flow, tasksConhecidas, agentsConhecidos, tarefasDetalhadas, diagnosticos);
1838
- }
1839
- for (const route of modulo.routes) {
1840
- validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos);
1841
- }
1842
- validarVinculos(modulo.vinculos, diagnosticos, `modulo ${modulo.nome}`);
1843
- for (const worker of modulo.workers) {
1844
- validarSuperficie(worker, "worker", tasksConhecidas, tiposConhecidos, diagnosticos);
1845
- }
1846
- for (const evento of modulo.eventos) {
1847
- validarSuperficie(evento, "evento", tasksConhecidas, tiposConhecidos, diagnosticos);
1848
- }
1849
- for (const fila of modulo.filas) {
1850
- validarSuperficie(fila, "fila", tasksConhecidas, tiposConhecidos, diagnosticos);
1851
- }
1852
- for (const cron of modulo.crons) {
1853
- validarSuperficie(cron, "cron", tasksConhecidas, tiposConhecidos, diagnosticos);
1854
- }
1855
- for (const webhook of modulo.webhooks) {
1856
- validarSuperficie(webhook, "webhook", tasksConhecidas, tiposConhecidos, diagnosticos);
1857
- }
1858
- for (const cache of modulo.caches) {
1859
- validarSuperficie(cache, "cache", tasksConhecidas, tiposConhecidos, diagnosticos);
1860
- }
1861
- for (const storage of modulo.storages) {
1862
- validarSuperficie(storage, "storage", tasksConhecidas, tiposConhecidos, diagnosticos);
1863
- }
1864
- for (const policy of modulo.policies) {
1865
- validarSuperficie(policy, "policy", tasksConhecidas, tiposConhecidos, diagnosticos);
1866
- }
1867
- for (const authorBlock of modulo.authorBlocks) {
1868
- validarAuthorBlock(authorBlock, diagnosticos);
1869
- }
1870
- for (const agent of modulo.agents) {
1871
- validarAgent(agent, diagnosticos);
1872
- }
1873
- validarAuthorSensivel(modulo, agentsConhecidos, diagnosticos);
1874
- for (const database of modulo.databases) {
1875
- validarDatabase(database, diagnosticos);
1876
- }
1877
- validarGuardrailsSeguranca(modulo, diagnosticos);
1878
- const assinaturasRoute = new Map();
1879
- for (const route of modulo.routes) {
1880
- const metodo = (localizarCampo(route.corpo, "metodo")?.valor ?? "").toUpperCase();
1881
- const caminho = recomporCaminhoRoute(localizarCampo(route.corpo, "caminho")) ?? "";
1882
- if (!metodo || !caminho) {
1883
- continue;
1884
- }
1885
- const chave = `${metodo} ${caminho}`;
1886
- const existente = assinaturasRoute.get(chave);
1887
- if (existente) {
1888
- 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."));
1889
- continue;
1890
- }
1891
- assinaturasRoute.set(chave, route);
1892
- }
1893
- for (const state of modulo.states) {
1894
- validarState(state, tiposConhecidos, enumsConhecidos, diagnosticos);
1895
- }
1896
- return {
1897
- contexto: {
1898
- modulo: modulo.nome,
1899
- simbolos,
1900
- tiposConhecidos,
1901
- tasksConhecidas,
1902
- agentsConhecidos,
1903
- tarefasDetalhadas,
1904
- statesConhecidos,
1905
- modulosImportados,
1906
- interoperabilidades,
1907
- enumsConhecidos,
1908
- },
1909
- diagnosticos,
1910
- };
1911
- }
1912
- //# sourceMappingURL=analisador.js.map