@semacode/cli 1.3.7 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/README.md +61 -41
- package/dist/drift.d.ts +98 -4
- package/dist/drift.js +1881 -546
- package/dist/drift.js.map +1 -1
- package/dist/importador.d.ts +2 -0
- package/dist/importador.js +166 -7
- package/dist/importador.js.map +1 -1
- package/dist/index.js +108 -9
- package/dist/index.js.map +1 -1
- package/dist/projeto.d.ts +6 -1
- package/dist/projeto.js.map +1 -1
- package/docs/cli.md +101 -0
- package/docs/instalacao-e-primeiro-uso.md +109 -196
- package/docs/integracao-com-ia.md +47 -198
- package/docs/persistencia-vendor-first.md +145 -0
- package/docs/sintaxe.md +67 -251
- package/exemplos/persistencia_vendor_first.sema +86 -0
- package/node_modules/@sema/gerador-css/package.json +1 -1
- package/node_modules/@sema/gerador-dart/package.json +1 -1
- package/node_modules/@sema/gerador-html/package.json +1 -1
- package/node_modules/@sema/gerador-javascript/package.json +1 -1
- package/node_modules/@sema/gerador-lua/package.json +1 -1
- package/node_modules/@sema/gerador-python/package.json +1 -1
- package/node_modules/@sema/gerador-typescript/package.json +1 -1
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +2 -1
- package/node_modules/@sema/nucleo/dist/formatador/index.js +32 -17
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/index.d.ts +1 -0
- package/node_modules/@sema/nucleo/dist/index.js +1 -0
- package/node_modules/@sema/nucleo/dist/index.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +94 -0
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +60 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +15 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/parser/parser.js +98 -3
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.d.ts +39 -0
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js +294 -0
- package/node_modules/@sema/nucleo/dist/persistencia/contratos.js.map +1 -0
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +118 -2
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
- package/node_modules/@sema/nucleo/package.json +1 -1
- package/node_modules/@sema/padroes/package.json +1 -1
- package/package.json +11 -11
package/dist/drift.js
CHANGED
|
@@ -8,7 +8,7 @@ import { extrairRotasJava, extrairSimbolosJava } from "./java-http.js";
|
|
|
8
8
|
import { contarIndentacaoPython, extrairRotasFlaskDecoradas, normalizarCaminhoFlask } from "./python-http.js";
|
|
9
9
|
import { extrairRotasRust, extrairSimbolosRust } from "./rust-http.js";
|
|
10
10
|
import { extrairRotasTypeScriptHttp } from "./typescript-http.js";
|
|
11
|
-
const
|
|
11
|
+
const DIRETORIOS_IGNORADOS_BASE = new Set([
|
|
12
12
|
".git",
|
|
13
13
|
".hg",
|
|
14
14
|
".svn",
|
|
@@ -25,9 +25,563 @@ const DIRETORIOS_IGNORADOS = new Set([
|
|
|
25
25
|
".tmp",
|
|
26
26
|
"generated",
|
|
27
27
|
]);
|
|
28
|
+
const DIRETORIOS_WORKTREE = [
|
|
29
|
+
".claude",
|
|
30
|
+
"worktrees",
|
|
31
|
+
];
|
|
32
|
+
const DIRETORIOS_CONSUMIDOR_LATERAL = [
|
|
33
|
+
"showcase",
|
|
34
|
+
"showcases",
|
|
35
|
+
"storybook",
|
|
36
|
+
"stories",
|
|
37
|
+
"playground",
|
|
38
|
+
"sandbox",
|
|
39
|
+
"fixture",
|
|
40
|
+
"fixtures",
|
|
41
|
+
"demo",
|
|
42
|
+
"demos",
|
|
43
|
+
"sample",
|
|
44
|
+
"samples",
|
|
45
|
+
"mini-web",
|
|
46
|
+
];
|
|
47
|
+
const TERMOS_ESCopo_IGNORADOS = new Set([
|
|
48
|
+
"api",
|
|
49
|
+
"app",
|
|
50
|
+
"apps",
|
|
51
|
+
"base",
|
|
52
|
+
"codigo",
|
|
53
|
+
"config",
|
|
54
|
+
"controller",
|
|
55
|
+
"controllers",
|
|
56
|
+
"data",
|
|
57
|
+
"drift",
|
|
58
|
+
"flow",
|
|
59
|
+
"module",
|
|
60
|
+
"modulo",
|
|
61
|
+
"publico",
|
|
62
|
+
"route",
|
|
63
|
+
"routes",
|
|
64
|
+
"schema",
|
|
65
|
+
"sema",
|
|
66
|
+
"service",
|
|
67
|
+
"services",
|
|
68
|
+
"src",
|
|
69
|
+
"task",
|
|
70
|
+
"tasks",
|
|
71
|
+
"tests",
|
|
72
|
+
"ui",
|
|
73
|
+
"web",
|
|
74
|
+
]);
|
|
75
|
+
let diretoriosIgnoradosAtivos = new Set(DIRETORIOS_IGNORADOS_BASE);
|
|
28
76
|
function normalizarFragmentoArquivo(valor) {
|
|
29
77
|
return valor.replace(/\\/g, "/").replace(/^\.?\//, "").trim().toLowerCase();
|
|
30
78
|
}
|
|
79
|
+
function normalizarEscopoDrift(valor) {
|
|
80
|
+
if (valor === "arquivo" || valor === "modulo" || valor === "projeto") {
|
|
81
|
+
return valor;
|
|
82
|
+
}
|
|
83
|
+
return "modulo";
|
|
84
|
+
}
|
|
85
|
+
function resolverOpcoesDrift(opcoes) {
|
|
86
|
+
return {
|
|
87
|
+
escopo: normalizarEscopoDrift(opcoes?.escopo),
|
|
88
|
+
ignorarWorktrees: opcoes?.ignorarWorktrees !== false,
|
|
89
|
+
ignorarConsumidoresLaterais: opcoes?.ignorarConsumidoresLaterais !== false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function resolverDiretoriosIgnoradosAtivos(opcoes) {
|
|
93
|
+
const resolvidas = resolverOpcoesDrift(opcoes);
|
|
94
|
+
const diretorios = new Set(DIRETORIOS_IGNORADOS_BASE);
|
|
95
|
+
if (resolvidas.ignorarWorktrees) {
|
|
96
|
+
for (const diretorio of DIRETORIOS_WORKTREE) {
|
|
97
|
+
diretorios.add(diretorio);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (resolvidas.ignorarConsumidoresLaterais) {
|
|
101
|
+
for (const diretorio of DIRETORIOS_CONSUMIDOR_LATERAL) {
|
|
102
|
+
diretorios.add(diretorio);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return diretorios;
|
|
106
|
+
}
|
|
107
|
+
function quebrarTermosEscopo(valor) {
|
|
108
|
+
return paraIdentificadorModulo(valor)
|
|
109
|
+
.split("_")
|
|
110
|
+
.map((item) => item.trim())
|
|
111
|
+
.filter((item) => item.length >= 3 && !TERMOS_ESCopo_IGNORADOS.has(item));
|
|
112
|
+
}
|
|
113
|
+
function extrairTermosEscopoDrift(contexto, escopo) {
|
|
114
|
+
if (escopo === "projeto") {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const termos = new Set();
|
|
118
|
+
termos.add(paraIdentificadorModulo(path.basename(contexto.entradaResolvida, path.extname(contexto.entradaResolvida))));
|
|
119
|
+
for (const modulo of contexto.modulosSelecionados) {
|
|
120
|
+
const ir = modulo.resultado.ir;
|
|
121
|
+
if (!ir) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
for (const termo of quebrarTermosEscopo(ir.nome)) {
|
|
125
|
+
termos.add(termo);
|
|
126
|
+
}
|
|
127
|
+
for (const task of ir.tasks) {
|
|
128
|
+
for (const termo of quebrarTermosEscopo(task.nome)) {
|
|
129
|
+
termos.add(termo);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const route of ir.routes) {
|
|
133
|
+
for (const termo of quebrarTermosEscopo(route.nome)) {
|
|
134
|
+
termos.add(termo);
|
|
135
|
+
}
|
|
136
|
+
if (route.caminho) {
|
|
137
|
+
for (const termo of quebrarTermosEscopo(route.caminho)) {
|
|
138
|
+
termos.add(termo);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return [...termos].filter(Boolean).sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
144
|
+
}
|
|
145
|
+
function caminhoTemSegmentoIgnorado(arquivo, segmentos) {
|
|
146
|
+
const partes = normalizarFragmentoArquivo(arquivo).split("/").filter(Boolean);
|
|
147
|
+
return partes.some((parte) => segmentos.includes(parte));
|
|
148
|
+
}
|
|
149
|
+
function normalizarCaminhoComparacao(caminhoArquivo) {
|
|
150
|
+
return path.resolve(caminhoArquivo).replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
function caminhoEstaDentroDe(base, alvo) {
|
|
153
|
+
const baseNormalizada = normalizarCaminhoComparacao(base);
|
|
154
|
+
const alvoNormalizado = normalizarCaminhoComparacao(alvo);
|
|
155
|
+
return alvoNormalizado === baseNormalizada || alvoNormalizado.startsWith(`${baseNormalizada}/`);
|
|
156
|
+
}
|
|
157
|
+
function resolverRaizEscopoReal(contexto) {
|
|
158
|
+
const entrada = path.resolve(contexto.entradaResolvida);
|
|
159
|
+
return path.extname(entrada) ? path.dirname(entrada) : entrada;
|
|
160
|
+
}
|
|
161
|
+
function resolverRaizesExplicitasConfiguradas(contexto) {
|
|
162
|
+
const configCarregada = contexto.configCarregada;
|
|
163
|
+
if (!configCarregada) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const origensDeclaradas = configCarregada.config.origens ?? (configCarregada.config.origem ? [configCarregada.config.origem] : []);
|
|
167
|
+
return [...new Set([
|
|
168
|
+
...(configCarregada.config.diretoriosCodigo ?? []).map((diretorio) => path.resolve(configCarregada.baseDiretorio, diretorio)),
|
|
169
|
+
...origensDeclaradas.map((origem) => path.resolve(configCarregada.baseDiretorio, origem)),
|
|
170
|
+
])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
171
|
+
}
|
|
172
|
+
function resolverRaizesIgnoradasPermitidas(contexto, segmentosIgnorados) {
|
|
173
|
+
return [...new Set([
|
|
174
|
+
resolverRaizEscopoReal(contexto),
|
|
175
|
+
...resolverRaizesExplicitasConfiguradas(contexto),
|
|
176
|
+
])]
|
|
177
|
+
.filter((raiz) => caminhoTemSegmentoIgnorado(raiz, segmentosIgnorados))
|
|
178
|
+
.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
179
|
+
}
|
|
180
|
+
function caminhoIgnoradoForaDoEscopoReal(caminhoArquivo, segmentosIgnorados, raizesPermitidas) {
|
|
181
|
+
if (!caminhoTemSegmentoIgnorado(caminhoArquivo, segmentosIgnorados)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
if (raizesPermitidas.length === 0) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return !raizesPermitidas.some((raiz) => caminhoEstaDentroDe(raiz, caminhoArquivo));
|
|
188
|
+
}
|
|
189
|
+
function filtrarCaminhosEscopoReal(caminhos, contexto, configuracao) {
|
|
190
|
+
const raizesWorktreePermitidas = resolverRaizesIgnoradasPermitidas(contexto, DIRETORIOS_WORKTREE);
|
|
191
|
+
const raizesConsumidorPermitidas = resolverRaizesIgnoradasPermitidas(contexto, DIRETORIOS_CONSUMIDOR_LATERAL);
|
|
192
|
+
return caminhos.filter((caminho) => {
|
|
193
|
+
if (configuracao.ignorarWorktrees && caminhoIgnoradoForaDoEscopoReal(caminho, DIRETORIOS_WORKTREE, raizesWorktreePermitidas)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (configuracao.ignorarConsumidoresLaterais
|
|
197
|
+
&& caminhoIgnoradoForaDoEscopoReal(caminho, DIRETORIOS_CONSUMIDOR_LATERAL, raizesConsumidorPermitidas)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function resolverDiretoriosCodigoEscopoReal(contexto, configuracao) {
|
|
204
|
+
return filtrarCaminhosEscopoReal(contexto.diretoriosCodigo, contexto, configuracao);
|
|
205
|
+
}
|
|
206
|
+
function textoCombinaEscopo(texto, termos) {
|
|
207
|
+
if (termos.length === 0) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
const normalizado = paraIdentificadorModulo(texto);
|
|
211
|
+
return termos.some((termo) => normalizado.includes(termo));
|
|
212
|
+
}
|
|
213
|
+
function filtrarConsumerSurfacesPorEscopo(consumerSurfaces, consumerBridges, contexto, configuracao) {
|
|
214
|
+
const raizesWorktreePermitidas = resolverRaizesIgnoradasPermitidas(contexto, DIRETORIOS_WORKTREE);
|
|
215
|
+
const raizesConsumidorPermitidas = resolverRaizesIgnoradasPermitidas(contexto, DIRETORIOS_CONSUMIDOR_LATERAL);
|
|
216
|
+
const manterSurface = (surface) => {
|
|
217
|
+
if (configuracao.ignorarWorktrees
|
|
218
|
+
&& caminhoIgnoradoForaDoEscopoReal(surface.arquivo, DIRETORIOS_WORKTREE, raizesWorktreePermitidas)) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (configuracao.ignorarConsumidoresLaterais
|
|
222
|
+
&& caminhoIgnoradoForaDoEscopoReal(surface.arquivo, DIRETORIOS_CONSUMIDOR_LATERAL, raizesConsumidorPermitidas)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
const combinaEscopo = textoCombinaEscopo(`${surface.rota} ${surface.arquivo} ${surface.tipoArquivo}`, configuracao.termosEscopo);
|
|
226
|
+
if (!configuracao.ignorarConsumidoresLaterais) {
|
|
227
|
+
return combinaEscopo || configuracao.escopo === "projeto";
|
|
228
|
+
}
|
|
229
|
+
return configuracao.escopo === "projeto" ? true : combinaEscopo;
|
|
230
|
+
};
|
|
231
|
+
const manterBridge = (bridge) => {
|
|
232
|
+
if (configuracao.ignorarWorktrees
|
|
233
|
+
&& caminhoIgnoradoForaDoEscopoReal(bridge.arquivo, DIRETORIOS_WORKTREE, raizesWorktreePermitidas)) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
if (configuracao.ignorarConsumidoresLaterais
|
|
237
|
+
&& caminhoIgnoradoForaDoEscopoReal(bridge.arquivo, DIRETORIOS_CONSUMIDOR_LATERAL, raizesConsumidorPermitidas)) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
const combinaEscopo = textoCombinaEscopo(`${bridge.caminho} ${bridge.arquivo} ${bridge.simbolo}`, configuracao.termosEscopo);
|
|
241
|
+
if (!configuracao.ignorarConsumidoresLaterais) {
|
|
242
|
+
return combinaEscopo || configuracao.escopo === "projeto";
|
|
243
|
+
}
|
|
244
|
+
return configuracao.escopo === "projeto" ? true : combinaEscopo;
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
consumerSurfaces: consumerSurfaces.filter(manterSurface),
|
|
248
|
+
consumerBridges: consumerBridges.filter(manterBridge),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const NOMES_RECURSO_IGNORADOS = new Set([
|
|
252
|
+
"all",
|
|
253
|
+
"and",
|
|
254
|
+
"as",
|
|
255
|
+
"by",
|
|
256
|
+
"create",
|
|
257
|
+
"delete",
|
|
258
|
+
"from",
|
|
259
|
+
"group",
|
|
260
|
+
"inner",
|
|
261
|
+
"into",
|
|
262
|
+
"join",
|
|
263
|
+
"left",
|
|
264
|
+
"limit",
|
|
265
|
+
"offset",
|
|
266
|
+
"on",
|
|
267
|
+
"or",
|
|
268
|
+
"order",
|
|
269
|
+
"outer",
|
|
270
|
+
"returning",
|
|
271
|
+
"right",
|
|
272
|
+
"select",
|
|
273
|
+
"set",
|
|
274
|
+
"table",
|
|
275
|
+
"update",
|
|
276
|
+
"values",
|
|
277
|
+
"view",
|
|
278
|
+
"where",
|
|
279
|
+
]);
|
|
280
|
+
const OPERACOES_REDIS_KEYSPACE = [
|
|
281
|
+
"append",
|
|
282
|
+
"decr",
|
|
283
|
+
"del",
|
|
284
|
+
"expire",
|
|
285
|
+
"expireat",
|
|
286
|
+
"get",
|
|
287
|
+
"getdel",
|
|
288
|
+
"getex",
|
|
289
|
+
"getrange",
|
|
290
|
+
"hdel",
|
|
291
|
+
"hexists",
|
|
292
|
+
"hget",
|
|
293
|
+
"hgetall",
|
|
294
|
+
"hincrby",
|
|
295
|
+
"hkeys",
|
|
296
|
+
"hlen",
|
|
297
|
+
"hmget",
|
|
298
|
+
"hmset",
|
|
299
|
+
"hrandfield",
|
|
300
|
+
"hscan",
|
|
301
|
+
"hset",
|
|
302
|
+
"hsetnx",
|
|
303
|
+
"hvals",
|
|
304
|
+
"incr",
|
|
305
|
+
"incrby",
|
|
306
|
+
"lindex",
|
|
307
|
+
"llen",
|
|
308
|
+
"lpop",
|
|
309
|
+
"lpush",
|
|
310
|
+
"lrange",
|
|
311
|
+
"lrem",
|
|
312
|
+
"lset",
|
|
313
|
+
"rpop",
|
|
314
|
+
"rpush",
|
|
315
|
+
"sadd",
|
|
316
|
+
"scard",
|
|
317
|
+
"set",
|
|
318
|
+
"setex",
|
|
319
|
+
"setnx",
|
|
320
|
+
"smembers",
|
|
321
|
+
"spop",
|
|
322
|
+
"srem",
|
|
323
|
+
"ttl",
|
|
324
|
+
"type",
|
|
325
|
+
"zadd",
|
|
326
|
+
"zcard",
|
|
327
|
+
"zrange",
|
|
328
|
+
"zrem",
|
|
329
|
+
];
|
|
330
|
+
const OPERACOES_REDIS_STREAM = [
|
|
331
|
+
"xadd",
|
|
332
|
+
"xdel",
|
|
333
|
+
"xgroupcreate",
|
|
334
|
+
"xgroupdestroy",
|
|
335
|
+
"xlen",
|
|
336
|
+
"xrange",
|
|
337
|
+
"xread",
|
|
338
|
+
"xreadgroup",
|
|
339
|
+
"xrevrange",
|
|
340
|
+
"xtrim",
|
|
341
|
+
];
|
|
342
|
+
function limparLiteralRecurso(valor) {
|
|
343
|
+
return valor
|
|
344
|
+
.trim()
|
|
345
|
+
.replace(/^["'`]+|["'`]+$/g, "")
|
|
346
|
+
.replace(/\$\{[^}]+\}/g, "")
|
|
347
|
+
.replace(/\{[^}]+\}/g, "")
|
|
348
|
+
.replace(/%[sdifjo]/gi, "")
|
|
349
|
+
.trim();
|
|
350
|
+
}
|
|
351
|
+
function fecharPrefixoRecurso(valor) {
|
|
352
|
+
return valor.replace(/[:/_\-.]+$/g, "").trim();
|
|
353
|
+
}
|
|
354
|
+
function normalizarNomeRecursoDrift(valor) {
|
|
355
|
+
return fecharPrefixoRecurso(limparLiteralRecurso(valor))
|
|
356
|
+
.normalize("NFD")
|
|
357
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
358
|
+
.toLowerCase()
|
|
359
|
+
.replace(/["'`]/g, "")
|
|
360
|
+
.replace(/\s+/g, "");
|
|
361
|
+
}
|
|
362
|
+
function variantesNomeRecursoDrift(valor) {
|
|
363
|
+
const base = fecharPrefixoRecurso(limparLiteralRecurso(valor));
|
|
364
|
+
if (!base) {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
const variantes = new Set();
|
|
368
|
+
const registrar = (candidato) => {
|
|
369
|
+
if (!candidato) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const normalizado = normalizarNomeRecursoDrift(candidato);
|
|
373
|
+
if (normalizado) {
|
|
374
|
+
variantes.add(normalizado);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
registrar(base);
|
|
378
|
+
registrar(base.replace(/[.:/_-]+/g, "_"));
|
|
379
|
+
registrar(base.replace(/[.:/_-]+/g, ""));
|
|
380
|
+
const partes = base.split(/[.:/_-]+/).filter(Boolean);
|
|
381
|
+
if (partes.length > 1) {
|
|
382
|
+
registrar(partes.join("_"));
|
|
383
|
+
registrar(partes.join(""));
|
|
384
|
+
}
|
|
385
|
+
const singular = base.replace(/s$/i, "");
|
|
386
|
+
if (singular && singular !== base) {
|
|
387
|
+
registrar(singular);
|
|
388
|
+
}
|
|
389
|
+
else if (!/s$/i.test(base)) {
|
|
390
|
+
registrar(`${base}s`);
|
|
391
|
+
}
|
|
392
|
+
return [...variantes];
|
|
393
|
+
}
|
|
394
|
+
function recursoEhIgnorado(nome) {
|
|
395
|
+
const normalizado = normalizarNomeRecursoDrift(nome);
|
|
396
|
+
if (!normalizado || normalizado.length < 2) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
return NOMES_RECURSO_IGNORADOS.has(normalizado);
|
|
400
|
+
}
|
|
401
|
+
function registrarRecursoDrift(recursos, origem, tipo, nome, arquivo, simbolo) {
|
|
402
|
+
const nomeLimpo = fecharPrefixoRecurso(limparLiteralRecurso(nome));
|
|
403
|
+
if (!nomeLimpo || recursoEhIgnorado(nomeLimpo)) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const chave = `${origem}:${tipo}:${normalizarNomeRecursoDrift(nomeLimpo)}:${arquivo}:${simbolo ?? ""}`;
|
|
407
|
+
if (!recursos.has(chave)) {
|
|
408
|
+
recursos.set(chave, {
|
|
409
|
+
origem,
|
|
410
|
+
nome: nomeLimpo,
|
|
411
|
+
arquivo,
|
|
412
|
+
simbolo,
|
|
413
|
+
tipo,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function inferirMotoresRelacionais(codigo, arquivo) {
|
|
418
|
+
const motores = new Set();
|
|
419
|
+
const caminho = normalizarFragmentoArquivo(arquivo);
|
|
420
|
+
if (/\b(?:from|require)\s*\(?["'`]pg["'`]/i.test(codigo)
|
|
421
|
+
|| /\bpostgres(?:ql)?\b/i.test(codigo)
|
|
422
|
+
|| /\bon\s+conflict\b/i.test(codigo)
|
|
423
|
+
|| /\breturning\b/i.test(codigo)
|
|
424
|
+
|| /\bjsonb\b/i.test(codigo)
|
|
425
|
+
|| /\bilike\b/i.test(codigo)
|
|
426
|
+
|| /(?:^|\/)(?:postgres|pgsql)(?:\/|[-_.])/i.test(caminho)) {
|
|
427
|
+
motores.add("postgres");
|
|
428
|
+
}
|
|
429
|
+
if (/\b(?:from|require)\s*\(?["'`](?:mysql2?(?:\/promise)?|mysql)["'`]/i.test(codigo)
|
|
430
|
+
|| /\bon\s+duplicate\s+key\b/i.test(codigo)
|
|
431
|
+
|| /\bauto_increment\b/i.test(codigo)
|
|
432
|
+
|| /\binnodb\b/i.test(codigo)
|
|
433
|
+
|| /\bunsigned\b/i.test(codigo)
|
|
434
|
+
|| /(?:^|\/)mysql(?:\/|[-_.])/i.test(caminho)) {
|
|
435
|
+
motores.add("mysql");
|
|
436
|
+
}
|
|
437
|
+
if (/\b(?:from|require)\s*\(?["'`](?:sqlite3|better-sqlite3|bun:sqlite|sqlite)["'`]/i.test(codigo)
|
|
438
|
+
|| /\bpragma\b/i.test(codigo)
|
|
439
|
+
|| /\bwithout\s+rowid\b/i.test(codigo)
|
|
440
|
+
|| /\bsqlite\b/i.test(codigo)
|
|
441
|
+
|| /(?:^|\/)sqlite(?:\/|[-_.])/i.test(caminho)) {
|
|
442
|
+
motores.add("sqlite");
|
|
443
|
+
}
|
|
444
|
+
const temSqlGenerico = /\b(?:select\b[\s\S]*?\bfrom\b|insert\s+into|update\s+[A-Za-z_][\w$.-]*\s+set|delete\s+from|create\s+(?:table|view)|alter\s+table|drop\s+(?:table|view)|join\s+[A-Za-z_][\w$.-]*)/i.test(codigo)
|
|
445
|
+
|| /\.(?:from|into|table)\s*\(\s*["'`]/i.test(codigo)
|
|
446
|
+
|| /\b(?:knex|db|trx)\s*\(\s*["'`][A-Za-z_][^"'`]+["'`]\s*\)/i.test(codigo)
|
|
447
|
+
|| /\bprisma\.[A-Za-z_]\w*\.(?:find\w+|create|update|delete|upsert|aggregate|count)\b/i.test(codigo);
|
|
448
|
+
if (temSqlGenerico && motores.size === 0) {
|
|
449
|
+
motores.add("postgres");
|
|
450
|
+
motores.add("mysql");
|
|
451
|
+
motores.add("sqlite");
|
|
452
|
+
}
|
|
453
|
+
return [...motores];
|
|
454
|
+
}
|
|
455
|
+
function extrairRecursosSql(arquivo, codigo) {
|
|
456
|
+
const recursos = new Map();
|
|
457
|
+
const motores = inferirMotoresRelacionais(codigo, arquivo);
|
|
458
|
+
if (motores.length === 0) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
const registrarParaMotores = (tipo, nome) => {
|
|
462
|
+
for (const motor of motores) {
|
|
463
|
+
registrarRecursoDrift(recursos, motor, tipo, nome, arquivo);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const registrarTextoSql = (texto) => {
|
|
467
|
+
if (!/\b(?:select\b[\s\S]*?\bfrom\b|insert\s+into|update\s+[A-Za-z_][\w$.-]*\s+set|delete\s+from|create\s+(?:table|view)|alter\s+table|drop\s+(?:table|view)|join\s+[A-Za-z_][\w$.-]*|create\s+(?:unique\s+)?index)\b/i.test(texto)) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
for (const match of texto.matchAll(/\bcreate\s+(?:or\s+replace\s+)?(table|view)\s+(?:if\s+not\s+exists\s+)?["'`]?([A-Za-z_][\w$.-]*)["'`]?/gi)) {
|
|
471
|
+
registrarParaMotores(match[1].toLowerCase(), match[2]);
|
|
472
|
+
}
|
|
473
|
+
for (const match of texto.matchAll(/\bcreate\s+(?:unique\s+)?index\s+(?:if\s+not\s+exists\s+)?["'`]?([A-Za-z_][\w$.-]*)["'`]?/gi)) {
|
|
474
|
+
registrarParaMotores("index", match[1]);
|
|
475
|
+
}
|
|
476
|
+
for (const match of texto.matchAll(/\b(?:insert\s+into|update|from|join|delete\s+from|truncate\s+table)\s+["'`]?([A-Za-z_][\w$.-]*)["'`]?/gi)) {
|
|
477
|
+
registrarParaMotores("table", match[1]);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
if (/\.(?:sql|psql|ddl)$/i.test(arquivo)) {
|
|
481
|
+
registrarTextoSql(codigo);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
for (const literal of codigo.matchAll(/(["'`])([\s\S]*?)\1/g)) {
|
|
485
|
+
registrarTextoSql(literal[2] ?? "");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
for (const match of codigo.matchAll(/\.(?:from|into|table)\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/gi)) {
|
|
489
|
+
registrarParaMotores("table", match[1]);
|
|
490
|
+
}
|
|
491
|
+
for (const match of codigo.matchAll(/\b(?:knex|db|trx)\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/gi)) {
|
|
492
|
+
registrarParaMotores("table", match[1]);
|
|
493
|
+
}
|
|
494
|
+
for (const match of codigo.matchAll(/\bprisma\.([A-Za-z_]\w*)\.(?:find\w+|create|update|delete|upsert|aggregate|count)\b/gi)) {
|
|
495
|
+
registrarParaMotores("table", match[1]);
|
|
496
|
+
}
|
|
497
|
+
return [...recursos.values()];
|
|
498
|
+
}
|
|
499
|
+
function extrairRecursosMongoDb(arquivo, codigo) {
|
|
500
|
+
const recursos = new Map();
|
|
501
|
+
const contextoMongo = /\b(?:mongodb|mongoose|mongoclient|objectid)\b/i.test(codigo)
|
|
502
|
+
|| /\bdb\.collection\s*\(/i.test(codigo)
|
|
503
|
+
|| /(?:^|\/)mongo(?:db)?(?:\/|[-_.])/i.test(normalizarFragmentoArquivo(arquivo));
|
|
504
|
+
if (!contextoMongo) {
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
for (const match of codigo.matchAll(/\b(?:db\.)?collection\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/gi)) {
|
|
508
|
+
registrarRecursoDrift(recursos, "mongodb", "collection", match[1], arquivo);
|
|
509
|
+
}
|
|
510
|
+
for (const match of codigo.matchAll(/\bgetCollection\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/gi)) {
|
|
511
|
+
registrarRecursoDrift(recursos, "mongodb", "collection", match[1], arquivo);
|
|
512
|
+
}
|
|
513
|
+
for (const match of codigo.matchAll(/\bmongoose\.model\s*\(\s*["'`]([^"'`]+)["'`](?:\s*,[\s\S]*?,\s*["'`]([^"'`]+)["'`])?/gi)) {
|
|
514
|
+
registrarRecursoDrift(recursos, "mongodb", "document", match[1], arquivo);
|
|
515
|
+
if (match[2]) {
|
|
516
|
+
registrarRecursoDrift(recursos, "mongodb", "collection", match[2], arquivo);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
for (const match of codigo.matchAll(/\bdb\.([A-Za-z_]\w*)\.(?:find|findOne|aggregate|insertOne|insertMany|updateOne|updateMany|deleteOne|deleteMany|countDocuments)\b/gi)) {
|
|
520
|
+
registrarRecursoDrift(recursos, "mongodb", "collection", match[1], arquivo);
|
|
521
|
+
}
|
|
522
|
+
return [...recursos.values()];
|
|
523
|
+
}
|
|
524
|
+
function extrairRecursosRedis(arquivo, codigo) {
|
|
525
|
+
const recursos = new Map();
|
|
526
|
+
const contextoRedis = /\b(?:from|require)\s*\(?["'`](?:redis|ioredis)["'`]/i.test(codigo)
|
|
527
|
+
|| /\bcreateClient\s*\(/i.test(codigo)
|
|
528
|
+
|| /\bx(?:add|read|readgroup|groupcreate|groupdestroy)\s*\(/i.test(codigo)
|
|
529
|
+
|| /(?:^|\/)redis(?:\/|[-_.])/i.test(normalizarFragmentoArquivo(arquivo));
|
|
530
|
+
if (!contextoRedis) {
|
|
531
|
+
return [];
|
|
532
|
+
}
|
|
533
|
+
const operacoesKeyspace = OPERACOES_REDIS_KEYSPACE.join("|");
|
|
534
|
+
const operacoesStream = OPERACOES_REDIS_STREAM.join("|");
|
|
535
|
+
const padraoKeyspace = new RegExp(`\\b(?:${operacoesKeyspace})\\s*\\(\\s*['"\\\`]([^'"\\\`]+)['"\\\`]`, "gi");
|
|
536
|
+
const padraoStream = new RegExp(`\\b(?:${operacoesStream})\\s*\\(\\s*['"\\\`]([^'"\\\`]+)['"\\\`]`, "gi");
|
|
537
|
+
for (const match of codigo.matchAll(padraoKeyspace)) {
|
|
538
|
+
registrarRecursoDrift(recursos, "redis", "keyspace", match[1], arquivo);
|
|
539
|
+
}
|
|
540
|
+
for (const match of codigo.matchAll(padraoStream)) {
|
|
541
|
+
registrarRecursoDrift(recursos, "redis", "stream", match[1], arquivo);
|
|
542
|
+
}
|
|
543
|
+
return [...recursos.values()];
|
|
544
|
+
}
|
|
545
|
+
function extrairRecursosPersistenciaCodigoVivo(arquivo, codigo) {
|
|
546
|
+
const recursos = new Map();
|
|
547
|
+
for (const recurso of extrairColecoesFirebase(arquivo, codigo)) {
|
|
548
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
549
|
+
}
|
|
550
|
+
for (const recurso of extrairRecursosSql(arquivo, codigo)) {
|
|
551
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
552
|
+
}
|
|
553
|
+
for (const recurso of extrairRecursosMongoDb(arquivo, codigo)) {
|
|
554
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
555
|
+
}
|
|
556
|
+
for (const recurso of extrairRecursosRedis(arquivo, codigo)) {
|
|
557
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
558
|
+
}
|
|
559
|
+
return [...recursos.values()];
|
|
560
|
+
}
|
|
561
|
+
function extrairRecursosPrisma(arquivo, codigo) {
|
|
562
|
+
const recursos = new Map();
|
|
563
|
+
const provider = codigo.match(/\bprovider\s*=\s*["'`](postgresql|mysql|sqlite)["'`]/i)?.[1]?.toLowerCase();
|
|
564
|
+
const origem = provider === "postgresql"
|
|
565
|
+
? "postgres"
|
|
566
|
+
: provider === "mysql"
|
|
567
|
+
? "mysql"
|
|
568
|
+
: provider === "sqlite"
|
|
569
|
+
? "sqlite"
|
|
570
|
+
: undefined;
|
|
571
|
+
if (!origem) {
|
|
572
|
+
return [];
|
|
573
|
+
}
|
|
574
|
+
for (const match of codigo.matchAll(/\bmodel\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\n\}/g)) {
|
|
575
|
+
const nomeModelo = match[1];
|
|
576
|
+
const corpo = match[2] ?? "";
|
|
577
|
+
const tabelaMapeada = corpo.match(/@@map\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/)?.[1];
|
|
578
|
+
registrarRecursoDrift(recursos, origem, "table", tabelaMapeada ?? nomeModelo, arquivo);
|
|
579
|
+
if (tabelaMapeada) {
|
|
580
|
+
registrarRecursoDrift(recursos, origem, "table", nomeModelo, arquivo);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return [...recursos.values()];
|
|
584
|
+
}
|
|
31
585
|
function escolherArquivoPorVinculo(arquivos, valor) {
|
|
32
586
|
const normalizado = normalizarFragmentoArquivo(valor);
|
|
33
587
|
const exato = arquivos.find((arquivo) => normalizarFragmentoArquivo(arquivo) === normalizado);
|
|
@@ -210,14 +764,35 @@ function resumirOperacional(resultado) {
|
|
|
210
764
|
? Math.round(resultado.tasks.reduce((total, task) => total + task.scoreSemantico, 0) / resultado.tasks.length)
|
|
211
765
|
: 0;
|
|
212
766
|
const confiancaGeral = scoreMedio >= 80 ? "alta" : scoreMedio >= 55 ? "media" : "baixa";
|
|
213
|
-
const riscosPrincipais = [...new Set(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
767
|
+
const riscosPrincipais = [...new Set([
|
|
768
|
+
...resultado.tasks.filter((task) => task.riscoOperacional !== "baixo").map((task) => `${task.task}:${task.riscoOperacional}`),
|
|
769
|
+
...resultado.persistencia_real
|
|
770
|
+
.filter((item) => item.status !== "materializado")
|
|
771
|
+
.map((item) => `${item.task}:${item.alvo}:persistencia_${item.status}`),
|
|
772
|
+
])];
|
|
773
|
+
const oQueTocar = [...new Set([
|
|
774
|
+
...resultado.tasks.flatMap((task) => task.arquivosProvaveisEditar),
|
|
775
|
+
...resultado.persistencia_real.flatMap((item) => [...item.arquivos, ...item.repositorios]),
|
|
776
|
+
])].slice(0, 20);
|
|
777
|
+
const oQueValidar = [...new Set([
|
|
778
|
+
...resultado.tasks.flatMap((task) => task.checksSugeridos),
|
|
779
|
+
...resultado.persistencia_real
|
|
780
|
+
.filter((item) => item.status !== "materializado")
|
|
781
|
+
.map((item) => `validar persistencia real de ${item.task} em ${item.alvo}`),
|
|
782
|
+
])];
|
|
783
|
+
const oQueEstaFrouxo = [...new Set([
|
|
784
|
+
...resultado.tasks.flatMap((task) => task.lacunas),
|
|
785
|
+
...resultado.persistencia_real
|
|
786
|
+
.filter((item) => item.status !== "materializado" || item.compatibilidade === "desconhecida" || item.compatibilidade === "invalido")
|
|
787
|
+
.map((item) => `persistencia:${item.alvo}:${item.status}:${item.compatibilidade}`),
|
|
788
|
+
])];
|
|
217
789
|
const oQueFoiInferido = [
|
|
218
790
|
...new Set([
|
|
219
791
|
...resultado.impls_quebrados.flatMap((impl) => impl.candidatos?.map((candidato) => candidato.caminho) ?? []),
|
|
220
792
|
...resultado.vinculos_quebrados.filter((vinculo) => vinculo.status === "parcial").map((vinculo) => `${vinculo.dono}:${vinculo.valor}`),
|
|
793
|
+
...resultado.persistencia_real
|
|
794
|
+
.filter((item) => item.compatibilidade === "desconhecida")
|
|
795
|
+
.map((item) => `${item.task}:${item.alvo}:compatibilidade_nao_confirmada`),
|
|
221
796
|
]),
|
|
222
797
|
];
|
|
223
798
|
return {
|
|
@@ -286,7 +861,7 @@ async function listarArquivosRecursivos(diretorio, extensoes) {
|
|
|
286
861
|
}
|
|
287
862
|
const encontrados = [];
|
|
288
863
|
for (const entrada of entradas) {
|
|
289
|
-
if (
|
|
864
|
+
if (diretoriosIgnoradosAtivos.has(entrada.name.toLowerCase())) {
|
|
290
865
|
continue;
|
|
291
866
|
}
|
|
292
867
|
const caminhoAtual = path.join(diretorio, entrada.name);
|
|
@@ -701,8 +1276,8 @@ async function indexarTypeScript(diretorios) {
|
|
|
701
1276
|
const sourceFile = ts.createSourceFile(arquivo, codigo, ts.ScriptTarget.Latest, true, scriptKind);
|
|
702
1277
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
703
1278
|
const relacao = path.relative(diretorio, arquivo);
|
|
704
|
-
for (const recurso of
|
|
705
|
-
recursos.
|
|
1279
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1280
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
706
1281
|
}
|
|
707
1282
|
for (const rota of extrairRotasTypeScriptHttp(sourceFile, relacao)) {
|
|
708
1283
|
rotas.push({
|
|
@@ -889,6 +1464,7 @@ function registrarRotasPython(rotas, decoratorsPendentes, prefixo, arquivo, nome
|
|
|
889
1464
|
async function indexarPython(diretorios) {
|
|
890
1465
|
const simbolos = new Map();
|
|
891
1466
|
const rotas = [];
|
|
1467
|
+
const recursos = new Map();
|
|
892
1468
|
for (const diretorio of diretorios) {
|
|
893
1469
|
const arquivos = (await listarArquivosRecursivos(diretorio, [".py"]))
|
|
894
1470
|
.filter((arquivo) => !arquivo.endsWith("__init__.py") && !/tests?[\\/]/i.test(arquivo));
|
|
@@ -896,6 +1472,9 @@ async function indexarPython(diretorios) {
|
|
|
896
1472
|
const texto = await readFile(arquivo, "utf8");
|
|
897
1473
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
898
1474
|
const prefixo = texto.match(/APIRouter\s*\(\s*prefix\s*=\s*["']([^"']+)["']/)?.[1];
|
|
1475
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, texto)) {
|
|
1476
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1477
|
+
}
|
|
899
1478
|
for (const rota of extrairRotasFlaskDecoradas(texto)) {
|
|
900
1479
|
rotas.push({
|
|
901
1480
|
origem: "flask",
|
|
@@ -947,11 +1526,12 @@ async function indexarPython(diretorios) {
|
|
|
947
1526
|
}
|
|
948
1527
|
}
|
|
949
1528
|
}
|
|
950
|
-
return { simbolos: [...simbolos.values()], rotas };
|
|
1529
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
951
1530
|
}
|
|
952
1531
|
async function indexarDart(diretorios) {
|
|
953
1532
|
const simbolos = new Map();
|
|
954
1533
|
const rotas = [];
|
|
1534
|
+
const recursos = new Map();
|
|
955
1535
|
const consumerSurfaces = new Map();
|
|
956
1536
|
for (const diretorio of diretorios) {
|
|
957
1537
|
const arquivos = (await listarArquivosRecursivos(diretorio, [".dart"]))
|
|
@@ -960,6 +1540,9 @@ async function indexarDart(diretorios) {
|
|
|
960
1540
|
const texto = await readFile(arquivo, "utf8");
|
|
961
1541
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
962
1542
|
const relacao = path.relative(diretorio, arquivo);
|
|
1543
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, texto)) {
|
|
1544
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1545
|
+
}
|
|
963
1546
|
for (const match of texto.matchAll(/(?:Future<[^\n]+>|[\w?<>.,\s]+)\s+(\w+)\(([^)]*)\)\s*(?:async\s*)?\{/g)) {
|
|
964
1547
|
const nome = match[1];
|
|
965
1548
|
if (["build", "toString"].includes(nome)) {
|
|
@@ -1006,6 +1589,7 @@ async function indexarDart(diretorios) {
|
|
|
1006
1589
|
return {
|
|
1007
1590
|
simbolos: [...simbolos.values()],
|
|
1008
1591
|
rotas,
|
|
1592
|
+
recursos: [...recursos.values()],
|
|
1009
1593
|
consumerSurfaces: [...consumerSurfaces.values()].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
|
|
1010
1594
|
|| a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
|
|
1011
1595
|
|| a.arquivo.localeCompare(b.arquivo, "pt-BR")),
|
|
@@ -1037,12 +1621,16 @@ function registrarSimboloGenerico(simbolos, origem, basesSimbolicas, arquivo, si
|
|
|
1037
1621
|
async function indexarDotnet(diretorios) {
|
|
1038
1622
|
const simbolos = new Map();
|
|
1039
1623
|
const rotas = [];
|
|
1624
|
+
const recursos = new Map();
|
|
1040
1625
|
for (const diretorio of diretorios) {
|
|
1041
1626
|
const arquivos = (await listarArquivosRecursivos(diretorio, [".cs"]))
|
|
1042
1627
|
.filter((arquivo) => !/(^|[\\/])(bin|obj|Test[s]?)([\\/]|$)/i.test(arquivo));
|
|
1043
1628
|
for (const arquivo of arquivos) {
|
|
1044
1629
|
const codigo = await readFile(arquivo, "utf8");
|
|
1045
1630
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
1631
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1632
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1633
|
+
}
|
|
1046
1634
|
for (const simbolo of extrairSimbolosDotnet(codigo)) {
|
|
1047
1635
|
registrarSimboloGenerico(simbolos, "cs", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
1048
1636
|
}
|
|
@@ -1057,17 +1645,21 @@ async function indexarDotnet(diretorios) {
|
|
|
1057
1645
|
}
|
|
1058
1646
|
}
|
|
1059
1647
|
}
|
|
1060
|
-
return { simbolos: [...simbolos.values()], rotas };
|
|
1648
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
1061
1649
|
}
|
|
1062
1650
|
async function indexarJava(diretorios) {
|
|
1063
1651
|
const simbolos = new Map();
|
|
1064
1652
|
const rotas = [];
|
|
1653
|
+
const recursos = new Map();
|
|
1065
1654
|
for (const diretorio of diretorios) {
|
|
1066
1655
|
const arquivos = (await listarArquivosRecursivos(diretorio, [".java"]))
|
|
1067
1656
|
.filter((arquivo) => !/(^|[\\/])(target|build|out|Test[s]?)([\\/]|$)/i.test(arquivo));
|
|
1068
1657
|
for (const arquivo of arquivos) {
|
|
1069
1658
|
const codigo = await readFile(arquivo, "utf8");
|
|
1070
1659
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
1660
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1661
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1662
|
+
}
|
|
1071
1663
|
for (const simbolo of extrairSimbolosJava(codigo)) {
|
|
1072
1664
|
registrarSimboloGenerico(simbolos, "java", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
1073
1665
|
}
|
|
@@ -1082,16 +1674,20 @@ async function indexarJava(diretorios) {
|
|
|
1082
1674
|
}
|
|
1083
1675
|
}
|
|
1084
1676
|
}
|
|
1085
|
-
return { simbolos: [...simbolos.values()], rotas };
|
|
1677
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
1086
1678
|
}
|
|
1087
1679
|
async function indexarGo(diretorios) {
|
|
1088
1680
|
const simbolos = new Map();
|
|
1089
1681
|
const rotas = [];
|
|
1682
|
+
const recursos = new Map();
|
|
1090
1683
|
for (const diretorio of diretorios) {
|
|
1091
1684
|
const arquivos = await listarArquivosRecursivos(diretorio, [".go"]);
|
|
1092
1685
|
for (const arquivo of arquivos) {
|
|
1093
1686
|
const codigo = await readFile(arquivo, "utf8");
|
|
1094
1687
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
1688
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1689
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1690
|
+
}
|
|
1095
1691
|
for (const simbolo of extrairSimbolosGo(codigo)) {
|
|
1096
1692
|
registrarSimboloGenerico(simbolos, "go", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
1097
1693
|
}
|
|
@@ -1106,16 +1702,20 @@ async function indexarGo(diretorios) {
|
|
|
1106
1702
|
}
|
|
1107
1703
|
}
|
|
1108
1704
|
}
|
|
1109
|
-
return { simbolos: [...simbolos.values()], rotas };
|
|
1705
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
1110
1706
|
}
|
|
1111
1707
|
async function indexarRust(diretorios) {
|
|
1112
1708
|
const simbolos = new Map();
|
|
1113
1709
|
const rotas = [];
|
|
1710
|
+
const recursos = new Map();
|
|
1114
1711
|
for (const diretorio of diretorios) {
|
|
1115
1712
|
const arquivos = await listarArquivosRecursivos(diretorio, [".rs"]);
|
|
1116
1713
|
for (const arquivo of arquivos) {
|
|
1117
1714
|
const codigo = await readFile(arquivo, "utf8");
|
|
1118
1715
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
1716
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1717
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1718
|
+
}
|
|
1119
1719
|
for (const simbolo of extrairSimbolosRust(codigo)) {
|
|
1120
1720
|
registrarSimboloGenerico(simbolos, "rust", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
1121
1721
|
}
|
|
@@ -1130,22 +1730,312 @@ async function indexarRust(diretorios) {
|
|
|
1130
1730
|
}
|
|
1131
1731
|
}
|
|
1132
1732
|
}
|
|
1133
|
-
return { simbolos: [...simbolos.values()], rotas };
|
|
1733
|
+
return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
|
|
1134
1734
|
}
|
|
1135
1735
|
async function indexarCpp(diretorios) {
|
|
1136
1736
|
const simbolos = new Map();
|
|
1737
|
+
const recursos = new Map();
|
|
1137
1738
|
for (const diretorio of diretorios) {
|
|
1138
1739
|
const arquivos = (await listarArquivosRecursivos(diretorio, [".cpp", ".cc", ".cxx", ".hpp", ".h"]))
|
|
1139
1740
|
.filter((arquivo) => !/(^|[\\/])(windows|linux|macos|runner|flutter|ephemeral|build|vendor)([\\/]|$)/i.test(arquivo));
|
|
1140
1741
|
for (const arquivo of arquivos) {
|
|
1141
1742
|
const codigo = await readFile(arquivo, "utf8");
|
|
1142
1743
|
const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
|
|
1744
|
+
for (const recurso of extrairRecursosPersistenciaCodigoVivo(arquivo, codigo)) {
|
|
1745
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1746
|
+
}
|
|
1143
1747
|
for (const simbolo of extrairSimbolosCpp(codigo)) {
|
|
1144
1748
|
registrarSimboloGenerico(simbolos, "cpp", basesSimbolicas, arquivo, simbolo.simbolo);
|
|
1145
1749
|
}
|
|
1146
1750
|
}
|
|
1147
1751
|
}
|
|
1148
|
-
return
|
|
1752
|
+
return {
|
|
1753
|
+
simbolos: [...simbolos.values()],
|
|
1754
|
+
recursos: [...recursos.values()],
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
async function indexarPersistenciaDeclarativa(diretorios) {
|
|
1758
|
+
const recursos = new Map();
|
|
1759
|
+
for (const diretorio of diretorios) {
|
|
1760
|
+
const arquivos = await listarArquivosRecursivos(diretorio, [".sql", ".psql", ".ddl", ".prisma"]);
|
|
1761
|
+
for (const arquivo of arquivos) {
|
|
1762
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
1763
|
+
const extracoes = arquivo.endsWith(".prisma")
|
|
1764
|
+
? extrairRecursosPrisma(arquivo, codigo)
|
|
1765
|
+
: extrairRecursosPersistenciaCodigoVivo(arquivo, codigo);
|
|
1766
|
+
for (const recurso of extracoes) {
|
|
1767
|
+
registrarRecursoDrift(recursos, recurso.origem, recurso.tipo, recurso.nome, recurso.arquivo, recurso.simbolo);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return { recursos: [...recursos.values()] };
|
|
1772
|
+
}
|
|
1773
|
+
function normalizarOrigemParaEngine(origem) {
|
|
1774
|
+
return origem && origem !== "firebase" ? origem : undefined;
|
|
1775
|
+
}
|
|
1776
|
+
function registrarColunaPersistenciaDrift(colunas, engine, recurso, coluna, arquivo) {
|
|
1777
|
+
const recursoNormalizado = fecharPrefixoRecurso(limparLiteralRecurso(recurso));
|
|
1778
|
+
const colunaNormalizada = fecharPrefixoRecurso(limparLiteralRecurso(coluna));
|
|
1779
|
+
if (!recursoNormalizado || !colunaNormalizada || recursoEhIgnorado(colunaNormalizada)) {
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
const chave = `${engine}:${normalizarNomeRecursoDrift(recursoNormalizado)}:${normalizarNomeRecursoDrift(colunaNormalizada)}:${arquivo}`;
|
|
1783
|
+
if (!colunas.has(chave)) {
|
|
1784
|
+
colunas.set(chave, {
|
|
1785
|
+
engine,
|
|
1786
|
+
recurso: recursoNormalizado,
|
|
1787
|
+
coluna: colunaNormalizada,
|
|
1788
|
+
arquivo,
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
function registrarRepositorioPersistenciaDrift(repositorios, engine, recurso, arquivo) {
|
|
1793
|
+
const recursoNormalizado = fecharPrefixoRecurso(limparLiteralRecurso(recurso));
|
|
1794
|
+
if (!recursoNormalizado) {
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
const chave = `${engine}:${normalizarNomeRecursoDrift(recursoNormalizado)}:${arquivo}`;
|
|
1798
|
+
if (!repositorios.has(chave)) {
|
|
1799
|
+
repositorios.set(chave, {
|
|
1800
|
+
engine,
|
|
1801
|
+
recurso: recursoNormalizado,
|
|
1802
|
+
arquivo,
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
function extrairColunasSqlDetalhadas(arquivo, codigo) {
|
|
1807
|
+
const colunas = new Map();
|
|
1808
|
+
const motores = inferirMotoresRelacionais(codigo, arquivo);
|
|
1809
|
+
if (motores.length === 0) {
|
|
1810
|
+
return [];
|
|
1811
|
+
}
|
|
1812
|
+
const registrarParaMotores = (recurso, coluna) => {
|
|
1813
|
+
for (const motor of motores) {
|
|
1814
|
+
registrarColunaPersistenciaDrift(colunas, motor, recurso, coluna, arquivo);
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
for (const match of codigo.matchAll(/\bcreate\s+table\s+(?:if\s+not\s+exists\s+)?["'`]?([A-Za-z_][\w$.-]*)["'`]?\s*\(([\s\S]*?)\)\s*;?/gi)) {
|
|
1818
|
+
const tabela = match[1];
|
|
1819
|
+
const corpo = match[2] ?? "";
|
|
1820
|
+
for (const linha of corpo.split(/\r?\n|,/)) {
|
|
1821
|
+
const trecho = linha.trim();
|
|
1822
|
+
if (!trecho || /^(?:constraint|primary|foreign|unique|check|key|index)\b/i.test(trecho)) {
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
const coluna = trecho.match(/^["'`]?([A-Za-z_][\w$.-]*)["'`]?/i)?.[1];
|
|
1826
|
+
if (coluna) {
|
|
1827
|
+
registrarParaMotores(tabela, coluna);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
for (const match of codigo.matchAll(/\binsert\s+into\s+["'`]?([A-Za-z_][\w$.-]*)["'`]?\s*\(([^)]+)\)/gi)) {
|
|
1832
|
+
for (const coluna of (match[2] ?? "").split(",").map((item) => item.trim())) {
|
|
1833
|
+
registrarParaMotores(match[1], coluna);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
for (const match of codigo.matchAll(/\bupdate\s+["'`]?([A-Za-z_][\w$.-]*)["'`]?\s+set\s+([\s\S]*?)(?:\bwhere\b|;|$)/gi)) {
|
|
1837
|
+
for (const coluna of (match[2] ?? "").split(",").map((item) => item.split("=")[0]?.trim() ?? "")) {
|
|
1838
|
+
registrarParaMotores(match[1], coluna);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
for (const match of codigo.matchAll(/\bselect\s+([\s\S]*?)\s+from\s+["'`]?([A-Za-z_][\w$.-]*)["'`]?/gi)) {
|
|
1842
|
+
const tabela = match[2];
|
|
1843
|
+
const lista = (match[1] ?? "").trim();
|
|
1844
|
+
if (!lista || lista === "*") {
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
for (const coluna of lista.split(",").map((item) => item.trim().split(/\s+as\s+/i)[0] ?? "")) {
|
|
1848
|
+
const nome = coluna.split(".").at(-1) ?? coluna;
|
|
1849
|
+
registrarParaMotores(tabela, nome);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return [...colunas.values()];
|
|
1853
|
+
}
|
|
1854
|
+
function extrairColunasPrismaDetalhadas(arquivo, codigo) {
|
|
1855
|
+
const colunas = new Map();
|
|
1856
|
+
const provider = codigo.match(/\bprovider\s*=\s*["'`](postgresql|mysql|sqlite)["'`]/i)?.[1]?.toLowerCase();
|
|
1857
|
+
const engine = provider === "postgresql"
|
|
1858
|
+
? "postgres"
|
|
1859
|
+
: provider === "mysql"
|
|
1860
|
+
? "mysql"
|
|
1861
|
+
: provider === "sqlite"
|
|
1862
|
+
? "sqlite"
|
|
1863
|
+
: undefined;
|
|
1864
|
+
if (!engine) {
|
|
1865
|
+
return [];
|
|
1866
|
+
}
|
|
1867
|
+
for (const match of codigo.matchAll(/\bmodel\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\n\}/g)) {
|
|
1868
|
+
const nomeModelo = match[1];
|
|
1869
|
+
const corpo = match[2] ?? "";
|
|
1870
|
+
const tabela = corpo.match(/@@map\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/)?.[1] ?? nomeModelo;
|
|
1871
|
+
for (const linha of corpo.split(/\r?\n/)) {
|
|
1872
|
+
const limpa = linha.trim();
|
|
1873
|
+
if (!limpa || limpa.startsWith("@@") || limpa.startsWith("//")) {
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
const coluna = limpa.match(/^([A-Za-z_]\w*)\s+/)?.[1];
|
|
1877
|
+
const colunaMapeada = limpa.match(/@map\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/)?.[1];
|
|
1878
|
+
if (coluna) {
|
|
1879
|
+
registrarColunaPersistenciaDrift(colunas, engine, tabela, colunaMapeada ?? coluna, arquivo);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
return [...colunas.values()];
|
|
1884
|
+
}
|
|
1885
|
+
function extrairCamposMongoDetalhados(arquivo, codigo) {
|
|
1886
|
+
const colunas = new Map();
|
|
1887
|
+
const colecoes = extrairRecursosMongoDb(arquivo, codigo).filter((item) => item.tipo === "collection");
|
|
1888
|
+
if (colecoes.length === 0) {
|
|
1889
|
+
return [];
|
|
1890
|
+
}
|
|
1891
|
+
const registrarCampoMongo = (colecao, trecho) => {
|
|
1892
|
+
for (const match of trecho.matchAll(/([A-Za-z_][\w$]*)\s*:/g)) {
|
|
1893
|
+
registrarColunaPersistenciaDrift(colunas, "mongodb", colecao, match[1], arquivo);
|
|
1894
|
+
}
|
|
1895
|
+
};
|
|
1896
|
+
for (const schema of codigo.matchAll(/\bnew\s+Schema\s*\(\s*\{([\s\S]*?)\}\s*\)/g)) {
|
|
1897
|
+
for (const colecao of colecoes) {
|
|
1898
|
+
registrarCampoMongo(colecao.nome, schema[1] ?? "");
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
for (const trecho of codigo.matchAll(/\b(?:find(?:One)?|update(?:One|Many)?|insertOne|insertMany)\s*\(\s*\{([\s\S]*?)\}\s*(?:,|\))/g)) {
|
|
1902
|
+
for (const colecao of colecoes) {
|
|
1903
|
+
registrarCampoMongo(colecao.nome, trecho[1] ?? "");
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return [...colunas.values()];
|
|
1907
|
+
}
|
|
1908
|
+
function extrairCamposRedisDetalhados(arquivo, codigo) {
|
|
1909
|
+
const colunas = new Map();
|
|
1910
|
+
for (const match of codigo.matchAll(/\bh(?:set|get|del|exists)\s*\(\s*["'`]([^"'`]+)["'`]\s*,\s*["'`]([^"'`]+)["'`]/gi)) {
|
|
1911
|
+
registrarColunaPersistenciaDrift(colunas, "redis", match[1], match[2], arquivo);
|
|
1912
|
+
}
|
|
1913
|
+
return [...colunas.values()];
|
|
1914
|
+
}
|
|
1915
|
+
function registrarRepositoriosPorRecursos(repositorios, arquivo, codigo, recursos) {
|
|
1916
|
+
const contextoRepositorio = /(?:repository|repositories|repositorio|repositorios|repo|dao|store|queries|persistence|persistencia)/i.test(arquivo)
|
|
1917
|
+
|| /\b(?:Repository|Repositories|Dao|Store)\b/.test(codigo);
|
|
1918
|
+
const contextoAcesso = /\b(?:select|insert|update|delete|aggregate|findOne|findMany|findUnique|findFirst|prisma\.|db\.collection|createClient|hset|hget|xadd|xread)\b/i.test(codigo);
|
|
1919
|
+
if (!contextoRepositorio && !contextoAcesso) {
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
for (const recurso of recursos) {
|
|
1923
|
+
const engine = normalizarOrigemParaEngine(recurso.origem);
|
|
1924
|
+
if (!engine) {
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
registrarRepositorioPersistenciaDrift(repositorios, engine, recurso.nome, arquivo);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
async function indexarPersistenciaDetalhada(diretorios) {
|
|
1931
|
+
const colunas = new Map();
|
|
1932
|
+
const repositorios = new Map();
|
|
1933
|
+
for (const diretorio of diretorios) {
|
|
1934
|
+
const arquivos = await listarArquivosRecursivos(diretorio, [
|
|
1935
|
+
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
1936
|
+
".py", ".dart", ".cs", ".java", ".go", ".rs", ".cpp", ".cc", ".cxx", ".hpp", ".h",
|
|
1937
|
+
".sql", ".psql", ".ddl", ".prisma",
|
|
1938
|
+
]);
|
|
1939
|
+
for (const arquivo of arquivos) {
|
|
1940
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
1941
|
+
const recursos = arquivo.endsWith(".prisma")
|
|
1942
|
+
? extrairRecursosPrisma(arquivo, codigo)
|
|
1943
|
+
: extrairRecursosPersistenciaCodigoVivo(arquivo, codigo);
|
|
1944
|
+
for (const coluna of extrairColunasSqlDetalhadas(arquivo, codigo)) {
|
|
1945
|
+
registrarColunaPersistenciaDrift(colunas, coluna.engine, coluna.recurso, coluna.coluna, coluna.arquivo);
|
|
1946
|
+
}
|
|
1947
|
+
for (const coluna of extrairColunasPrismaDetalhadas(arquivo, codigo)) {
|
|
1948
|
+
registrarColunaPersistenciaDrift(colunas, coluna.engine, coluna.recurso, coluna.coluna, coluna.arquivo);
|
|
1949
|
+
}
|
|
1950
|
+
for (const coluna of extrairCamposMongoDetalhados(arquivo, codigo)) {
|
|
1951
|
+
registrarColunaPersistenciaDrift(colunas, coluna.engine, coluna.recurso, coluna.coluna, coluna.arquivo);
|
|
1952
|
+
}
|
|
1953
|
+
for (const coluna of extrairCamposRedisDetalhados(arquivo, codigo)) {
|
|
1954
|
+
registrarColunaPersistenciaDrift(colunas, coluna.engine, coluna.recurso, coluna.coluna, coluna.arquivo);
|
|
1955
|
+
}
|
|
1956
|
+
registrarRepositoriosPorRecursos(repositorios, arquivo, codigo, recursos);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
return {
|
|
1960
|
+
colunas: [...colunas.values()],
|
|
1961
|
+
repositorios: [...repositorios.values()],
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
function recursoDetalhadoCombina(recurso, esperado) {
|
|
1965
|
+
return variantesNomeRecursoDrift(recurso).some((variante) => esperado.nomes.some((nome) => variantesNomeRecursoDrift(nome).includes(variante)));
|
|
1966
|
+
}
|
|
1967
|
+
function localizarCompatibilidadePersistencia(bancos, esperado, recursoReal) {
|
|
1968
|
+
for (const banco of bancos) {
|
|
1969
|
+
for (const recurso of banco.resources) {
|
|
1970
|
+
if (!recursoPersistenciaCombinaAlvo(recurso, esperado.alvo)) {
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
const engine = banco.engine ?? normalizarOrigemParaEngine(recursoReal?.origem);
|
|
1974
|
+
const compatibilidade = engine
|
|
1975
|
+
? recurso.compatibilidade.find((item) => item.engine === engine) ?? recurso.compatibilidade[0]
|
|
1976
|
+
: recurso.compatibilidade[0];
|
|
1977
|
+
return {
|
|
1978
|
+
engine: (engine ?? recursoReal?.origem ?? "desconhecido"),
|
|
1979
|
+
compatibilidade: compatibilidade?.status ?? "desconhecida",
|
|
1980
|
+
motivoCompatibilidade: compatibilidade?.motivo,
|
|
1981
|
+
tipo: recurso.resourceKind ?? recursoReal?.tipo ?? esperado.tiposAceitos[0] ?? "query",
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
return {
|
|
1986
|
+
engine: recursoReal?.origem ?? esperado.origem ?? "desconhecido",
|
|
1987
|
+
compatibilidade: "desconhecida",
|
|
1988
|
+
tipo: recursoReal?.tipo ?? esperado.tiposAceitos[0] ?? "query",
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
export async function analisarPersistenciaReal(contexto, mapaRecursos, detalhesPersistencia, opcoes) {
|
|
1992
|
+
const opcoesResolvidas = resolverOpcoesDrift(opcoes);
|
|
1993
|
+
const diretoriosCodigoAtivos = resolverDiretoriosCodigoEscopoReal(contexto, opcoesResolvidas);
|
|
1994
|
+
const mapa = mapaRecursos ?? construirMapaRecursos((await indexarPersistenciaDeclarativa(diretoriosCodigoAtivos)).recursos);
|
|
1995
|
+
const detalhes = detalhesPersistencia ?? await indexarPersistenciaDetalhada(diretoriosCodigoAtivos);
|
|
1996
|
+
const registros = [];
|
|
1997
|
+
for (const item of contexto.modulosSelecionados) {
|
|
1998
|
+
const ir = item.resultado.ir;
|
|
1999
|
+
if (!ir) {
|
|
2000
|
+
continue;
|
|
2001
|
+
}
|
|
2002
|
+
for (const task of ir.tasks) {
|
|
2003
|
+
for (const esperado of extrairRecursosEsperados(task, ir)) {
|
|
2004
|
+
const correspondencias = esperado.nomes.flatMap((nome) => variantesNomeRecursoDrift(nome).flatMap((variante) => (mapa.get(variante) ?? []).filter((recurso) => recursoResolvidoCombinaEsperado(recurso, esperado))));
|
|
2005
|
+
const recursosReais = [...new Map(correspondencias.map((recurso) => [`${recurso.origem}:${recurso.tipo}:${recurso.nome}:${recurso.arquivo}`, recurso])).values()];
|
|
2006
|
+
const compatibilidade = localizarCompatibilidadePersistencia(ir.databases, esperado, recursosReais[0]);
|
|
2007
|
+
const colunas = [...new Set(detalhes.colunas
|
|
2008
|
+
.filter((coluna) => (!normalizarOrigemParaEngine(recursosReais[0]?.origem) || coluna.engine === normalizarOrigemParaEngine(recursosReais[0]?.origem))
|
|
2009
|
+
&& recursoDetalhadoCombina(coluna.recurso, esperado))
|
|
2010
|
+
.map((coluna) => coluna.coluna))].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2011
|
+
const repositorios = [...new Set(detalhes.repositorios
|
|
2012
|
+
.filter((repositorio) => (!normalizarOrigemParaEngine(recursosReais[0]?.origem) || repositorio.engine === normalizarOrigemParaEngine(recursosReais[0]?.origem))
|
|
2013
|
+
&& recursoDetalhadoCombina(repositorio.recurso, esperado))
|
|
2014
|
+
.map((repositorio) => repositorio.arquivo))].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2015
|
+
const arquivos = [...new Set(recursosReais.map((recurso) => recurso.arquivo))].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2016
|
+
registros.push({
|
|
2017
|
+
modulo: ir.nome,
|
|
2018
|
+
task: task.nome,
|
|
2019
|
+
alvo: esperado.alvo,
|
|
2020
|
+
engine: compatibilidade.engine,
|
|
2021
|
+
tipo: compatibilidade.tipo,
|
|
2022
|
+
status: recursosReais.length === 0
|
|
2023
|
+
? "divergente"
|
|
2024
|
+
: colunas.length > 0 || repositorios.length > 0
|
|
2025
|
+
? "materializado"
|
|
2026
|
+
: "parcial",
|
|
2027
|
+
arquivos,
|
|
2028
|
+
colunas,
|
|
2029
|
+
repositorios,
|
|
2030
|
+
compatibilidade: compatibilidade.compatibilidade,
|
|
2031
|
+
motivoCompatibilidade: compatibilidade.motivoCompatibilidade,
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
return registros.sort((a, b) => a.modulo.localeCompare(b.modulo, "pt-BR")
|
|
2037
|
+
|| a.task.localeCompare(b.task, "pt-BR")
|
|
2038
|
+
|| a.alvo.localeCompare(b.alvo, "pt-BR"));
|
|
1149
2039
|
}
|
|
1150
2040
|
function normalizarCaminhoRota(caminho) {
|
|
1151
2041
|
if (!caminho) {
|
|
@@ -1337,16 +2227,121 @@ function escolherRotasEsperadas(task, fontesLegado) {
|
|
|
1337
2227
|
function taskEhBridgeFirebase(task) {
|
|
1338
2228
|
return task.implementacoesExternas.some((impl) => impl.origem === "ts" && /sema_contract_bridge|collections?|apps\.worker/i.test(impl.caminho));
|
|
1339
2229
|
}
|
|
1340
|
-
function
|
|
1341
|
-
|
|
1342
|
-
|
|
2230
|
+
function tiposAceitosParaRecursoPersistencia(recurso) {
|
|
2231
|
+
switch (recurso.resourceKind) {
|
|
2232
|
+
case "table":
|
|
2233
|
+
case "view":
|
|
2234
|
+
case "query":
|
|
2235
|
+
case "index":
|
|
2236
|
+
case "collection":
|
|
2237
|
+
case "document":
|
|
2238
|
+
case "keyspace":
|
|
2239
|
+
case "stream":
|
|
2240
|
+
return [recurso.resourceKind];
|
|
2241
|
+
default:
|
|
2242
|
+
return [];
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
function nomesRecursoPersistencia(recurso) {
|
|
2246
|
+
return [...new Set([
|
|
2247
|
+
recurso.nome,
|
|
2248
|
+
recurso.table,
|
|
2249
|
+
recurso.collection,
|
|
2250
|
+
recurso.entity,
|
|
2251
|
+
recurso.path,
|
|
2252
|
+
recurso.surface,
|
|
2253
|
+
].filter((item) => Boolean(item)))];
|
|
2254
|
+
}
|
|
2255
|
+
function recursoPersistenciaCombinaAlvo(recurso, alvo) {
|
|
2256
|
+
const alvoVariantes = new Set(variantesNomeRecursoDrift(alvo));
|
|
2257
|
+
if (alvoVariantes.size === 0) {
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2260
|
+
return nomesRecursoPersistencia(recurso).some((nome) => variantesNomeRecursoDrift(nome).some((variacao) => alvoVariantes.has(variacao)));
|
|
2261
|
+
}
|
|
2262
|
+
function extrairRecursosEsperados(task, ir) {
|
|
2263
|
+
const esperados = new Map();
|
|
2264
|
+
const registrar = (esperado) => {
|
|
2265
|
+
const chave = `${esperado.origem ?? "qualquer"}:${esperado.tiposAceitos.join(",")}:${esperado.nomes.join("|")}:${esperado.alvo}`;
|
|
2266
|
+
if (!esperados.has(chave)) {
|
|
2267
|
+
esperados.set(chave, esperado);
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
if (taskEhBridgeFirebase(task)) {
|
|
2271
|
+
for (const efeito of task.efeitosEstruturados.filter((item) => item.categoria === "persistencia" && Boolean(item.alvo))) {
|
|
2272
|
+
registrar({
|
|
2273
|
+
categoria: "persistencia",
|
|
2274
|
+
alvo: efeito.alvo,
|
|
2275
|
+
origem: "firebase",
|
|
2276
|
+
tiposAceitos: ["colecao"],
|
|
2277
|
+
nomes: [efeito.alvo],
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
const efeitosPersistencia = task.efeitosEstruturados.filter((efeito) => ["persistencia", "db.read", "db.write"].includes(efeito.categoria) && Boolean(efeito.alvo));
|
|
2282
|
+
if (efeitosPersistencia.length === 0 || ir.databases.length === 0) {
|
|
2283
|
+
return [...esperados.values()];
|
|
2284
|
+
}
|
|
2285
|
+
for (const efeito of efeitosPersistencia) {
|
|
2286
|
+
for (const database of ir.databases) {
|
|
2287
|
+
for (const recurso of database.resources) {
|
|
2288
|
+
const tiposAceitos = tiposAceitosParaRecursoPersistencia(recurso);
|
|
2289
|
+
if (tiposAceitos.length === 0 || !recursoPersistenciaCombinaAlvo(recurso, efeito.alvo)) {
|
|
2290
|
+
continue;
|
|
2291
|
+
}
|
|
2292
|
+
registrar({
|
|
2293
|
+
categoria: "persistencia",
|
|
2294
|
+
alvo: efeito.alvo,
|
|
2295
|
+
origem: database.engine,
|
|
2296
|
+
tiposAceitos,
|
|
2297
|
+
nomes: nomesRecursoPersistencia(recurso),
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return [...esperados.values()];
|
|
2303
|
+
}
|
|
2304
|
+
function construirMapaRecursos(recursos) {
|
|
2305
|
+
const mapa = new Map();
|
|
2306
|
+
for (const recurso of recursos) {
|
|
2307
|
+
for (const variante of variantesNomeRecursoDrift(recurso.nome)) {
|
|
2308
|
+
const existentes = mapa.get(variante) ?? [];
|
|
2309
|
+
if (!existentes.some((item) => item.origem === recurso.origem
|
|
2310
|
+
&& item.tipo === recurso.tipo
|
|
2311
|
+
&& item.arquivo === recurso.arquivo
|
|
2312
|
+
&& item.nome === recurso.nome
|
|
2313
|
+
&& item.simbolo === recurso.simbolo)) {
|
|
2314
|
+
existentes.push(recurso);
|
|
2315
|
+
mapa.set(variante, existentes);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return mapa;
|
|
2320
|
+
}
|
|
2321
|
+
function recursoResolvidoCombinaEsperado(recurso, esperado) {
|
|
2322
|
+
if (esperado.origem && recurso.origem !== esperado.origem) {
|
|
2323
|
+
return false;
|
|
2324
|
+
}
|
|
2325
|
+
if (esperado.tiposAceitos.length > 0 && !esperado.tiposAceitos.includes(recurso.tipo)) {
|
|
2326
|
+
return false;
|
|
2327
|
+
}
|
|
2328
|
+
const recursoVariantes = new Set(variantesNomeRecursoDrift(recurso.nome));
|
|
2329
|
+
return esperado.nomes.some((nome) => variantesNomeRecursoDrift(nome).some((variante) => recursoVariantes.has(variante)));
|
|
2330
|
+
}
|
|
2331
|
+
function resolverRecursoEsperado(mapaRecursos, esperado, arquivosPreferidos) {
|
|
2332
|
+
const candidatos = new Map();
|
|
2333
|
+
for (const nome of esperado.nomes) {
|
|
2334
|
+
for (const variante of variantesNomeRecursoDrift(nome)) {
|
|
2335
|
+
for (const recurso of mapaRecursos.get(variante) ?? []) {
|
|
2336
|
+
if (recursoResolvidoCombinaEsperado(recurso, esperado)) {
|
|
2337
|
+
candidatos.set(`${recurso.origem}:${recurso.tipo}:${recurso.nome}:${recurso.arquivo}:${recurso.simbolo ?? ""}`, recurso);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
1343
2341
|
}
|
|
1344
|
-
return
|
|
1345
|
-
.
|
|
1346
|
-
.
|
|
1347
|
-
categoria: "persistencia",
|
|
1348
|
-
alvo: efeito.alvo,
|
|
1349
|
-
}));
|
|
2342
|
+
return [...candidatos.values()].sort((a, b) => Number(Boolean(arquivosPreferidos?.has(b.arquivo))) - Number(Boolean(arquivosPreferidos?.has(a.arquivo)))
|
|
2343
|
+
|| a.arquivo.localeCompare(b.arquivo, "pt-BR")
|
|
2344
|
+
|| a.nome.localeCompare(b.nome, "pt-BR"))[0];
|
|
1350
2345
|
}
|
|
1351
2346
|
function coletarVinculosIr(ir) {
|
|
1352
2347
|
return [
|
|
@@ -1357,580 +2352,920 @@ function coletarVinculosIr(ir) {
|
|
|
1357
2352
|
...ir.superficies.flatMap((superficie) => superficie.vinculos.map((vinculo) => ({ donoTipo: "superficie", dono: `${superficie.tipo}:${superficie.nome}`, vinculo }))),
|
|
1358
2353
|
];
|
|
1359
2354
|
}
|
|
1360
|
-
export async function analisarDriftLegado(contexto) {
|
|
1361
|
-
const
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
const
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
...
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
if (!
|
|
2355
|
+
export async function analisarDriftLegado(contexto, opcoes) {
|
|
2356
|
+
const opcoesResolvidas = resolverOpcoesDrift(opcoes);
|
|
2357
|
+
const configuracaoEscopo = {
|
|
2358
|
+
escopo: opcoesResolvidas.escopo,
|
|
2359
|
+
ignorarWorktrees: opcoesResolvidas.ignorarWorktrees,
|
|
2360
|
+
ignorarConsumidoresLaterais: opcoesResolvidas.ignorarConsumidoresLaterais,
|
|
2361
|
+
termosEscopo: extrairTermosEscopoDrift(contexto, opcoesResolvidas.escopo),
|
|
2362
|
+
};
|
|
2363
|
+
const diretoriosIgnoradosAnteriores = diretoriosIgnoradosAtivos;
|
|
2364
|
+
diretoriosIgnoradosAtivos = resolverDiretoriosIgnoradosAtivos(opcoesResolvidas);
|
|
2365
|
+
try {
|
|
2366
|
+
const diretoriosCodigoAtivos = resolverDiretoriosCodigoEscopoReal(contexto, configuracaoEscopo);
|
|
2367
|
+
const indexTs = await indexarTypeScript(diretoriosCodigoAtivos);
|
|
2368
|
+
const indexPy = await indexarPython(diretoriosCodigoAtivos);
|
|
2369
|
+
const indexDart = await indexarDart(diretoriosCodigoAtivos);
|
|
2370
|
+
const indexDotnet = await indexarDotnet(diretoriosCodigoAtivos);
|
|
2371
|
+
const indexJava = await indexarJava(diretoriosCodigoAtivos);
|
|
2372
|
+
const indexGo = await indexarGo(diretoriosCodigoAtivos);
|
|
2373
|
+
const indexRust = await indexarRust(diretoriosCodigoAtivos);
|
|
2374
|
+
const indexPersistencia = await indexarPersistenciaDeclarativa(diretoriosCodigoAtivos);
|
|
2375
|
+
const detalhesPersistencia = await indexarPersistenciaDetalhada(diretoriosCodigoAtivos);
|
|
2376
|
+
const indexCpp = await indexarCpp(diretoriosCodigoAtivos);
|
|
2377
|
+
const todosSimbolos = [
|
|
2378
|
+
...indexTs.simbolos,
|
|
2379
|
+
...indexPy.simbolos,
|
|
2380
|
+
...indexDart.simbolos,
|
|
2381
|
+
...indexDotnet.simbolos,
|
|
2382
|
+
...indexJava.simbolos,
|
|
2383
|
+
...indexGo.simbolos,
|
|
2384
|
+
...indexRust.simbolos,
|
|
2385
|
+
...indexCpp.simbolos,
|
|
2386
|
+
];
|
|
2387
|
+
const mapaImpl = new Map([
|
|
2388
|
+
...indexTs.simbolos.map((item) => [item.caminho, item]),
|
|
2389
|
+
...indexPy.simbolos.map((item) => [item.caminho, item]),
|
|
2390
|
+
...indexDart.simbolos.map((item) => [item.caminho, item]),
|
|
2391
|
+
...indexDotnet.simbolos.map((item) => [item.caminho, item]),
|
|
2392
|
+
...indexJava.simbolos.map((item) => [item.caminho, item]),
|
|
2393
|
+
...indexGo.simbolos.map((item) => [item.caminho, item]),
|
|
2394
|
+
...indexRust.simbolos.map((item) => [item.caminho, item]),
|
|
2395
|
+
...indexCpp.simbolos.map((item) => [item.caminho, item]),
|
|
2396
|
+
]);
|
|
2397
|
+
const todosRecursos = [
|
|
2398
|
+
...indexTs.recursos,
|
|
2399
|
+
...indexPy.recursos,
|
|
2400
|
+
...indexDart.recursos,
|
|
2401
|
+
...indexDotnet.recursos,
|
|
2402
|
+
...indexJava.recursos,
|
|
2403
|
+
...indexGo.recursos,
|
|
2404
|
+
...indexRust.recursos,
|
|
2405
|
+
...indexCpp.recursos,
|
|
2406
|
+
...indexPersistencia.recursos,
|
|
2407
|
+
];
|
|
2408
|
+
const mapaRecursos = construirMapaRecursos(todosRecursos);
|
|
2409
|
+
const todasRotasIndexadas = [
|
|
2410
|
+
...indexTs.rotas,
|
|
2411
|
+
...indexPy.rotas,
|
|
2412
|
+
...indexDart.rotas,
|
|
2413
|
+
...indexDotnet.rotas,
|
|
2414
|
+
...indexJava.rotas,
|
|
2415
|
+
...indexGo.rotas,
|
|
2416
|
+
...indexRust.rotas,
|
|
2417
|
+
];
|
|
2418
|
+
const todosArquivosConhecidos = [...new Set([
|
|
2419
|
+
...todosSimbolos.map((item) => item.arquivo),
|
|
2420
|
+
...todasRotasIndexadas.map((item) => item.arquivo),
|
|
2421
|
+
...todosRecursos.map((item) => item.arquivo),
|
|
2422
|
+
])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2423
|
+
const implsValidos = [];
|
|
2424
|
+
const implsQuebrados = [];
|
|
2425
|
+
const vinculosValidos = [];
|
|
2426
|
+
const vinculosQuebrados = [];
|
|
2427
|
+
const rotasDivergentes = [];
|
|
2428
|
+
const recursosValidos = [];
|
|
2429
|
+
const recursosDivergentes = [];
|
|
2430
|
+
const diagnosticos = [];
|
|
2431
|
+
const tasksResumo = [];
|
|
2432
|
+
const taskPorChave = new Map();
|
|
2433
|
+
const guardrailsPorTask = new Map();
|
|
2434
|
+
const resumoVinculosPorTask = new Map();
|
|
2435
|
+
for (const item of contexto.modulosSelecionados) {
|
|
2436
|
+
const ir = item.resultado.ir;
|
|
2437
|
+
if (!ir) {
|
|
1443
2438
|
continue;
|
|
1444
2439
|
}
|
|
1445
|
-
const
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
if (!superficie.task || superficie.perfilCompatibilidade !== "publico") {
|
|
1465
|
-
continue;
|
|
2440
|
+
const superficiesPorChave = new Map(ir.superficies.map((superficie) => [`${superficie.tipo}:${superficie.nome}`, superficie]));
|
|
2441
|
+
for (const task of ir.tasks) {
|
|
2442
|
+
guardrailsPorTask.set(`${ir.nome}:${task.nome}`, {
|
|
2443
|
+
publica: false,
|
|
2444
|
+
sensivel: calcularRiscoOperacional(task) === "alto",
|
|
2445
|
+
auth: task.auth.explicita,
|
|
2446
|
+
authz: task.authz.explicita,
|
|
2447
|
+
dados: task.dados.explicita,
|
|
2448
|
+
audit: task.audit.explicita,
|
|
2449
|
+
segredos: task.segredos.explicita,
|
|
2450
|
+
forbidden: task.forbidden.explicita,
|
|
2451
|
+
dadosSensiveis: Boolean(task.dados.classificacaoPadrao && ["pii", "financeiro", "credencial", "segredo"].includes(task.dados.classificacaoPadrao)
|
|
2452
|
+
|| task.dados.campos.some((campo) => ["pii", "financeiro", "credencial", "segredo"].includes(campo.classificacao))),
|
|
2453
|
+
efeitoPrivilegiado: task.efeitosEstruturados.some((efeito) => ["db.read", "db.write", "queue.publish", "queue.consume", "fs.read", "fs.write", "network.egress", "secret.read", "shell.exec"].includes(efeito.categoria)
|
|
2454
|
+
|| ["alta", "critica"].includes(efeito.criticidade ?? "")),
|
|
2455
|
+
exigeSegredos: task.efeitosEstruturados.some((efeito) => efeito.categoria === "secret.read")
|
|
2456
|
+
|| Boolean(task.dados.classificacaoPadrao && ["credencial", "segredo"].includes(task.dados.classificacaoPadrao)
|
|
2457
|
+
|| task.dados.campos.some((campo) => ["credencial", "segredo"].includes(campo.classificacao))),
|
|
2458
|
+
});
|
|
1466
2459
|
}
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
guardrails
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
2460
|
+
for (const route of ir.routes) {
|
|
2461
|
+
if (!route.task || route.perfilCompatibilidade !== "publico") {
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
const guardrails = guardrailsPorTask.get(`${ir.nome}:${route.task}`);
|
|
2465
|
+
if (guardrails) {
|
|
2466
|
+
guardrails.publica = true;
|
|
2467
|
+
guardrails.auth = guardrails.auth || route.auth.explicita;
|
|
2468
|
+
guardrails.authz = guardrails.authz || route.authz.explicita;
|
|
2469
|
+
guardrails.dados = guardrails.dados || route.dados.explicita;
|
|
2470
|
+
guardrails.audit = guardrails.audit || route.audit.explicita;
|
|
2471
|
+
guardrails.segredos = guardrails.segredos || route.segredos.explicita;
|
|
2472
|
+
guardrails.forbidden = guardrails.forbidden || route.forbidden.explicita;
|
|
2473
|
+
guardrails.dadosSensiveis = guardrails.dadosSensiveis || Boolean(route.dados.classificacaoPadrao && ["pii", "financeiro", "credencial", "segredo"].includes(route.dados.classificacaoPadrao)
|
|
2474
|
+
|| route.dados.campos.some((campo) => ["pii", "financeiro", "credencial", "segredo"].includes(campo.classificacao)));
|
|
2475
|
+
guardrails.efeitoPrivilegiado = guardrails.efeitoPrivilegiado || route.efeitosPublicos.some((efeito) => ["db.read", "db.write", "queue.publish", "queue.consume", "fs.read", "fs.write", "network.egress", "secret.read", "shell.exec"].includes(efeito.categoria)
|
|
2476
|
+
|| ["alta", "critica"].includes(efeito.criticidade ?? ""));
|
|
2477
|
+
guardrails.exigeSegredos = guardrails.exigeSegredos || route.efeitosPublicos.some((efeito) => efeito.categoria === "secret.read")
|
|
2478
|
+
|| Boolean(route.dados.classificacaoPadrao && ["credencial", "segredo"].includes(route.dados.classificacaoPadrao)
|
|
2479
|
+
|| route.dados.campos.some((campo) => ["credencial", "segredo"].includes(campo.classificacao)));
|
|
2480
|
+
}
|
|
1483
2481
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
let validos = 0;
|
|
1488
|
-
let quebrados = 0;
|
|
1489
|
-
const arquivosReferenciados = new Set();
|
|
1490
|
-
const simbolosReferenciados = new Set();
|
|
1491
|
-
const candidatosTask = new Map();
|
|
1492
|
-
if (task.implementacoesExternas.length === 0) {
|
|
1493
|
-
for (const candidato of sugerirCandidatosParaTaskSemImpl(todosSimbolos, task.nome)) {
|
|
1494
|
-
candidatosTask.set(`${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`, candidato);
|
|
2482
|
+
for (const superficie of ir.superficies) {
|
|
2483
|
+
if (!superficie.task || superficie.perfilCompatibilidade !== "publico") {
|
|
2484
|
+
continue;
|
|
1495
2485
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
2486
|
+
const guardrails = guardrailsPorTask.get(`${ir.nome}:${superficie.task}`);
|
|
2487
|
+
if (guardrails) {
|
|
2488
|
+
guardrails.publica = true;
|
|
2489
|
+
guardrails.auth = guardrails.auth || superficie.auth.explicita;
|
|
2490
|
+
guardrails.authz = guardrails.authz || superficie.authz.explicita;
|
|
2491
|
+
guardrails.dados = guardrails.dados || superficie.dados.explicita;
|
|
2492
|
+
guardrails.audit = guardrails.audit || superficie.audit.explicita;
|
|
2493
|
+
guardrails.segredos = guardrails.segredos || superficie.segredos.explicita;
|
|
2494
|
+
guardrails.forbidden = guardrails.forbidden || superficie.forbidden.explicita;
|
|
2495
|
+
guardrails.dadosSensiveis = guardrails.dadosSensiveis || Boolean(superficie.dados.classificacaoPadrao && ["pii", "financeiro", "credencial", "segredo"].includes(superficie.dados.classificacaoPadrao)
|
|
2496
|
+
|| superficie.dados.campos.some((campo) => ["pii", "financeiro", "credencial", "segredo"].includes(campo.classificacao)));
|
|
2497
|
+
guardrails.efeitoPrivilegiado = guardrails.efeitoPrivilegiado || superficie.effects.some((efeito) => ["db.read", "db.write", "queue.publish", "queue.consume", "fs.read", "fs.write", "network.egress", "secret.read", "shell.exec"].includes(efeito.categoria)
|
|
2498
|
+
|| ["alta", "critica"].includes(efeito.criticidade ?? ""));
|
|
2499
|
+
guardrails.exigeSegredos = guardrails.exigeSegredos || superficie.effects.some((efeito) => efeito.categoria === "secret.read")
|
|
2500
|
+
|| Boolean(superficie.dados.classificacaoPadrao && ["credencial", "segredo"].includes(superficie.dados.classificacaoPadrao)
|
|
2501
|
+
|| superficie.dados.campos.some((campo) => ["credencial", "segredo"].includes(campo.classificacao)));
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
for (const task of ir.tasks) {
|
|
2505
|
+
taskPorChave.set(`${ir.nome}:${task.nome}`, task);
|
|
2506
|
+
let validos = 0;
|
|
2507
|
+
let quebrados = 0;
|
|
2508
|
+
const arquivosReferenciados = new Set();
|
|
2509
|
+
const simbolosReferenciados = new Set();
|
|
2510
|
+
const candidatosTask = new Map();
|
|
2511
|
+
if (task.implementacoesExternas.length === 0) {
|
|
2512
|
+
for (const candidato of sugerirCandidatosParaTaskSemImpl(todosSimbolos, task.nome)) {
|
|
2513
|
+
candidatosTask.set(`${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`, candidato);
|
|
2514
|
+
}
|
|
2515
|
+
diagnosticos.push({
|
|
2516
|
+
tipo: "task_sem_impl",
|
|
2517
|
+
modulo: ir.nome,
|
|
2518
|
+
task: task.nome,
|
|
2519
|
+
mensagem: `Task "${task.nome}" ainda nao foi ligada a nenhuma implementacao externa.`,
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
for (const impl of task.implementacoesExternas) {
|
|
2523
|
+
const resolvido = mapaImpl.get(impl.caminho);
|
|
2524
|
+
const registro = {
|
|
2525
|
+
modulo: ir.nome,
|
|
2526
|
+
task: task.nome,
|
|
2527
|
+
origem: impl.origem,
|
|
2528
|
+
caminho: impl.caminho,
|
|
2529
|
+
arquivo: resolvido?.arquivo,
|
|
2530
|
+
simbolo: resolvido?.simbolo,
|
|
2531
|
+
caminhoResolvido: resolvido?.caminho,
|
|
2532
|
+
status: resolvido ? "resolvido" : "quebrado",
|
|
2533
|
+
};
|
|
2534
|
+
if (resolvido) {
|
|
2535
|
+
arquivosReferenciados.add(resolvido.arquivo);
|
|
2536
|
+
simbolosReferenciados.add(resolvido.simbolo);
|
|
2537
|
+
implsValidos.push(registro);
|
|
2538
|
+
validos += 1;
|
|
2539
|
+
}
|
|
2540
|
+
else {
|
|
2541
|
+
registro.candidatos = sugerirCandidatosParaImpl(todosSimbolos, impl.origem, impl.caminho);
|
|
2542
|
+
for (const candidato of registro.candidatos) {
|
|
2543
|
+
candidatosTask.set(`${candidato.origem}:${candidato.caminho}:${candidato.arquivo}:${candidato.simbolo}`, candidato);
|
|
2544
|
+
}
|
|
2545
|
+
implsQuebrados.push(registro);
|
|
2546
|
+
quebrados += 1;
|
|
2547
|
+
diagnosticos.push({
|
|
2548
|
+
tipo: "impl_quebrado",
|
|
2549
|
+
modulo: ir.nome,
|
|
2550
|
+
task: task.nome,
|
|
2551
|
+
mensagem: `Implementacao externa "${impl.origem}:${impl.caminho}" nao foi encontrada nos diretorios de codigo vivos.`,
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
tasksResumo.push({
|
|
1498
2556
|
modulo: ir.nome,
|
|
1499
2557
|
task: task.nome,
|
|
1500
|
-
|
|
2558
|
+
impls: task.implementacoesExternas.length,
|
|
2559
|
+
implsValidos: validos,
|
|
2560
|
+
implsQuebrados: quebrados,
|
|
2561
|
+
semImplementacao: task.implementacoesExternas.length === 0,
|
|
2562
|
+
scoreSemantico: 0,
|
|
2563
|
+
confiancaVinculo: "baixa",
|
|
2564
|
+
riscoOperacional: "baixo",
|
|
2565
|
+
lacunas: [],
|
|
2566
|
+
arquivosReferenciados: [...arquivosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
2567
|
+
arquivosProvaveisEditar: [],
|
|
2568
|
+
simbolosReferenciados: [...simbolosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
2569
|
+
candidatosImpl: ordenarCandidatos([...candidatosTask.values()]).slice(0, 5),
|
|
2570
|
+
checksSugeridos: [],
|
|
1501
2571
|
});
|
|
2572
|
+
for (const recursoEsperado of extrairRecursosEsperados(task, ir)) {
|
|
2573
|
+
const resolvido = resolverRecursoEsperado(mapaRecursos, recursoEsperado, arquivosReferenciados);
|
|
2574
|
+
const registro = {
|
|
2575
|
+
modulo: ir.nome,
|
|
2576
|
+
task: task.nome,
|
|
2577
|
+
categoria: recursoEsperado.categoria,
|
|
2578
|
+
alvo: recursoEsperado.alvo,
|
|
2579
|
+
arquivo: resolvido?.arquivo ?? "",
|
|
2580
|
+
origem: resolvido?.origem ?? recursoEsperado.origem ?? "firebase",
|
|
2581
|
+
tipo: resolvido?.tipo ?? recursoEsperado.tiposAceitos[0] ?? "query",
|
|
2582
|
+
status: resolvido ? "resolvido" : "divergente",
|
|
2583
|
+
};
|
|
2584
|
+
if (resolvido) {
|
|
2585
|
+
registro.arquivo = resolvido.arquivo;
|
|
2586
|
+
recursosValidos.push(registro);
|
|
2587
|
+
}
|
|
2588
|
+
else {
|
|
2589
|
+
recursosDivergentes.push(registro);
|
|
2590
|
+
const escopo = recursoEsperado.origem ? `${recursoEsperado.origem}` : "persistencia declarada";
|
|
2591
|
+
diagnosticos.push({
|
|
2592
|
+
tipo: "recurso_divergente",
|
|
2593
|
+
modulo: ir.nome,
|
|
2594
|
+
task: task.nome,
|
|
2595
|
+
mensagem: `Recurso vivo "${recursoEsperado.alvo}" nao foi encontrado no codigo legado para ${escopo}.`,
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
1502
2599
|
}
|
|
1503
|
-
for (const
|
|
1504
|
-
const
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
2600
|
+
for (const route of ir.routes) {
|
|
2601
|
+
const taskAssociada = ir.tasks.find((task) => task.nome === route.task);
|
|
2602
|
+
const esperadas = escolherRotasEsperadas(taskAssociada ?? {
|
|
2603
|
+
nome: "",
|
|
2604
|
+
input: [],
|
|
2605
|
+
output: [],
|
|
2606
|
+
rules: [],
|
|
2607
|
+
regrasEstruturadas: [],
|
|
2608
|
+
effects: [],
|
|
2609
|
+
efeitosEstruturados: [],
|
|
2610
|
+
implementacoesExternas: [],
|
|
2611
|
+
vinculos: [],
|
|
2612
|
+
execucao: {
|
|
2613
|
+
idempotencia: false,
|
|
2614
|
+
timeout: "padrao",
|
|
2615
|
+
retry: "nenhum",
|
|
2616
|
+
compensacao: "nenhuma",
|
|
2617
|
+
criticidadeOperacional: "media",
|
|
2618
|
+
explicita: false,
|
|
2619
|
+
},
|
|
2620
|
+
auth: {
|
|
2621
|
+
explicita: false,
|
|
2622
|
+
},
|
|
2623
|
+
authz: {
|
|
2624
|
+
explicita: false,
|
|
2625
|
+
papeis: [],
|
|
2626
|
+
escopos: [],
|
|
2627
|
+
},
|
|
2628
|
+
dados: {
|
|
2629
|
+
explicita: false,
|
|
2630
|
+
campos: [],
|
|
2631
|
+
},
|
|
2632
|
+
audit: {
|
|
2633
|
+
explicita: false,
|
|
2634
|
+
},
|
|
2635
|
+
segredos: {
|
|
2636
|
+
explicita: false,
|
|
2637
|
+
itens: [],
|
|
2638
|
+
},
|
|
2639
|
+
forbidden: {
|
|
2640
|
+
explicita: false,
|
|
2641
|
+
regras: [],
|
|
2642
|
+
},
|
|
2643
|
+
guarantees: [],
|
|
2644
|
+
garantiasEstruturadas: [],
|
|
2645
|
+
errors: {},
|
|
2646
|
+
errosDetalhados: [],
|
|
2647
|
+
perfilCompatibilidade: "interno",
|
|
2648
|
+
resumoAgente: {
|
|
2649
|
+
riscos: [],
|
|
2650
|
+
checks: [],
|
|
2651
|
+
entidadesAfetadas: [],
|
|
2652
|
+
superficiesPublicas: [],
|
|
2653
|
+
mutacoesPrevistas: [],
|
|
2654
|
+
},
|
|
2655
|
+
tests: [],
|
|
2656
|
+
}, contexto.fontesLegado);
|
|
2657
|
+
if (!esperadas.length || !route.metodo || !route.caminho) {
|
|
2658
|
+
continue;
|
|
1520
2659
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
2660
|
+
const encontradas = todasRotasIndexadas.filter((rotaResolvida) => rotaResolvida.origem !== "nextjs-consumer"
|
|
2661
|
+
&& rotaResolvida.origem !== "react-vite-consumer"
|
|
2662
|
+
&& rotaResolvida.origem !== "angular-consumer"
|
|
2663
|
+
&& rotaResolvida.origem !== "flutter-consumer"
|
|
2664
|
+
&& esperadas.includes(rotaResolvida.origem));
|
|
2665
|
+
const combina = encontradas.some((rotaResolvida) => rotaResolvida.metodo === route.metodo
|
|
2666
|
+
&& normalizarCaminhoRota(rotaResolvida.caminho) === normalizarCaminhoRota(route.caminho));
|
|
2667
|
+
if (!combina) {
|
|
2668
|
+
const registro = {
|
|
2669
|
+
modulo: ir.nome,
|
|
2670
|
+
route: route.nome,
|
|
2671
|
+
metodo: route.metodo,
|
|
2672
|
+
caminho: route.caminho,
|
|
2673
|
+
motivo: `Nenhuma rota publica ${route.metodo} ${route.caminho} foi encontrada no codigo legado para o framework esperado.`,
|
|
2674
|
+
};
|
|
2675
|
+
rotasDivergentes.push(registro);
|
|
1528
2676
|
diagnosticos.push({
|
|
1529
|
-
tipo: "
|
|
2677
|
+
tipo: "rota_divergente",
|
|
1530
2678
|
modulo: ir.nome,
|
|
1531
|
-
|
|
1532
|
-
mensagem:
|
|
2679
|
+
route: route.nome,
|
|
2680
|
+
mensagem: registro.motivo,
|
|
1533
2681
|
});
|
|
1534
2682
|
}
|
|
1535
2683
|
}
|
|
1536
|
-
|
|
1537
|
-
modulo: ir.nome,
|
|
1538
|
-
task: task.nome,
|
|
1539
|
-
impls: task.implementacoesExternas.length,
|
|
1540
|
-
implsValidos: validos,
|
|
1541
|
-
implsQuebrados: quebrados,
|
|
1542
|
-
semImplementacao: task.implementacoesExternas.length === 0,
|
|
1543
|
-
scoreSemantico: 0,
|
|
1544
|
-
confiancaVinculo: "baixa",
|
|
1545
|
-
riscoOperacional: "baixo",
|
|
1546
|
-
lacunas: [],
|
|
1547
|
-
arquivosReferenciados: [...arquivosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
1548
|
-
arquivosProvaveisEditar: [],
|
|
1549
|
-
simbolosReferenciados: [...simbolosReferenciados].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
1550
|
-
candidatosImpl: ordenarCandidatos([...candidatosTask.values()]).slice(0, 5),
|
|
1551
|
-
checksSugeridos: [],
|
|
1552
|
-
});
|
|
1553
|
-
for (const recursoEsperado of extrairRecursosEsperados(task)) {
|
|
1554
|
-
const resolvido = mapaRecursos.get(recursoEsperado.alvo);
|
|
2684
|
+
for (const itemVinculo of coletarVinculosIr(ir)) {
|
|
1555
2685
|
const registro = {
|
|
1556
2686
|
modulo: ir.nome,
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
status: resolvido ? "resolvido" : "divergente",
|
|
2687
|
+
donoTipo: itemVinculo.donoTipo,
|
|
2688
|
+
dono: itemVinculo.dono,
|
|
2689
|
+
tipo: itemVinculo.vinculo.tipo,
|
|
2690
|
+
valor: itemVinculo.vinculo.valor,
|
|
2691
|
+
status: "nao_encontrado",
|
|
2692
|
+
confianca: "baixa",
|
|
1564
2693
|
};
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
2694
|
+
const arquivoDeclarado = itemVinculo.vinculo.arquivo ?? (itemVinculo.vinculo.tipo === "arquivo" ? itemVinculo.vinculo.valor : undefined);
|
|
2695
|
+
const simboloDeclarado = itemVinculo.vinculo.simbolo ?? (itemVinculo.vinculo.tipo === "simbolo" ? itemVinculo.vinculo.valor : undefined);
|
|
2696
|
+
const recursoDeclarado = itemVinculo.vinculo.recurso ?? (["recurso", "tabela", "fila", "cache", "storage"].includes(itemVinculo.vinculo.tipo) ? itemVinculo.vinculo.valor : undefined);
|
|
2697
|
+
const superficieDeclarada = itemVinculo.vinculo.superficie ?? (["superficie", "rota", "worker", "cron", "webhook", "evento", "policy", "fila", "cache", "storage"].includes(itemVinculo.vinculo.tipo) ? itemVinculo.vinculo.valor : undefined);
|
|
2698
|
+
if (simboloDeclarado) {
|
|
2699
|
+
const resolucaoSimbolo = escolherSimboloPorVinculo(todosSimbolos, mapaImpl, simboloDeclarado);
|
|
2700
|
+
registro.status = resolucaoSimbolo.status;
|
|
2701
|
+
registro.confianca = resolucaoSimbolo.confianca;
|
|
2702
|
+
registro.arquivo = resolucaoSimbolo.simbolo?.arquivo;
|
|
2703
|
+
registro.simbolo = resolucaoSimbolo.simbolo?.simbolo;
|
|
2704
|
+
}
|
|
2705
|
+
else if (arquivoDeclarado) {
|
|
2706
|
+
const resolucaoArquivo = escolherArquivoPorVinculo(todosArquivosConhecidos, arquivoDeclarado);
|
|
2707
|
+
registro.status = resolucaoArquivo.status;
|
|
2708
|
+
registro.confianca = resolucaoArquivo.confianca;
|
|
2709
|
+
registro.arquivo = resolucaoArquivo.arquivo;
|
|
2710
|
+
}
|
|
2711
|
+
else if (recursoDeclarado) {
|
|
2712
|
+
const recurso = resolverRecursoEsperado(mapaRecursos, {
|
|
2713
|
+
categoria: "persistencia",
|
|
2714
|
+
alvo: recursoDeclarado,
|
|
2715
|
+
tiposAceitos: [],
|
|
2716
|
+
nomes: [recursoDeclarado],
|
|
2717
|
+
});
|
|
2718
|
+
if (recurso) {
|
|
2719
|
+
registro.status = "resolvido";
|
|
2720
|
+
registro.confianca = "alta";
|
|
2721
|
+
registro.arquivo = recurso.arquivo;
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
else if (superficieDeclarada) {
|
|
2725
|
+
const rota = todasRotasIndexadas.find((rotaResolvida) => normalizarCaminhoRota(rotaResolvida.caminho) === normalizarCaminhoRota(superficieDeclarada));
|
|
2726
|
+
if (rota) {
|
|
2727
|
+
registro.status = "resolvido";
|
|
2728
|
+
registro.confianca = "alta";
|
|
2729
|
+
registro.arquivo = rota.arquivo;
|
|
2730
|
+
registro.simbolo = rota.simbolo;
|
|
2731
|
+
}
|
|
2732
|
+
else {
|
|
2733
|
+
const resolucaoArquivo = escolherArquivoPorVinculo(todosArquivosConhecidos, superficieDeclarada);
|
|
2734
|
+
registro.status = resolucaoArquivo.status;
|
|
2735
|
+
registro.confianca = resolucaoArquivo.confianca;
|
|
2736
|
+
registro.arquivo = resolucaoArquivo.arquivo;
|
|
2737
|
+
}
|
|
1568
2738
|
}
|
|
1569
2739
|
else {
|
|
1570
|
-
|
|
2740
|
+
const resolucaoArquivo = escolherArquivoPorVinculo(todosArquivosConhecidos, itemVinculo.vinculo.valor);
|
|
2741
|
+
registro.status = resolucaoArquivo.status;
|
|
2742
|
+
registro.confianca = resolucaoArquivo.confianca;
|
|
2743
|
+
registro.arquivo = resolucaoArquivo.arquivo;
|
|
2744
|
+
}
|
|
2745
|
+
if (registro.status === "nao_encontrado" && itemVinculo.donoTipo === "superficie") {
|
|
2746
|
+
const superficie = superficiesPorChave.get(itemVinculo.dono);
|
|
2747
|
+
const ancora = superficie
|
|
2748
|
+
? encontrarAncoraSuperficie(ir, superficie, todosSimbolos, mapaImpl, todosArquivosConhecidos)
|
|
2749
|
+
: undefined;
|
|
2750
|
+
if (ancora) {
|
|
2751
|
+
registro.status = "parcial";
|
|
2752
|
+
registro.confianca = ancora.confianca === "alta" ? "media" : ancora.confianca;
|
|
2753
|
+
registro.arquivo = registro.arquivo ?? ancora.arquivo;
|
|
2754
|
+
registro.simbolo = registro.simbolo ?? ancora.simbolo;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
if (registro.status === "nao_encontrado") {
|
|
2758
|
+
vinculosQuebrados.push(registro);
|
|
1571
2759
|
diagnosticos.push({
|
|
1572
|
-
tipo: "
|
|
2760
|
+
tipo: "vinculo_quebrado",
|
|
1573
2761
|
modulo: ir.nome,
|
|
1574
|
-
|
|
1575
|
-
|
|
2762
|
+
mensagem: `Vinculo ${registro.tipo}="${registro.valor}" de ${registro.donoTipo} "${registro.dono}" nao foi resolvido no codigo vivo.`,
|
|
2763
|
+
...(itemVinculo.donoTipo === "task" ? { task: itemVinculo.dono } : itemVinculo.donoTipo === "route" ? { route: itemVinculo.dono } : {}),
|
|
1576
2764
|
});
|
|
1577
2765
|
}
|
|
2766
|
+
else {
|
|
2767
|
+
vinculosValidos.push(registro);
|
|
2768
|
+
}
|
|
2769
|
+
if (itemVinculo.donoTipo === "task") {
|
|
2770
|
+
const chaveTask = `${ir.nome}:${itemVinculo.dono}`;
|
|
2771
|
+
const resumo = resumoVinculosPorTask.get(chaveTask) ?? {
|
|
2772
|
+
validos: 0,
|
|
2773
|
+
quebrados: 0,
|
|
2774
|
+
arquivos: new Set(),
|
|
2775
|
+
};
|
|
2776
|
+
if (registro.status === "nao_encontrado") {
|
|
2777
|
+
resumo.quebrados += 1;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
resumo.validos += 1;
|
|
2781
|
+
}
|
|
2782
|
+
if (registro.arquivo) {
|
|
2783
|
+
resumo.arquivos.add(registro.arquivo);
|
|
2784
|
+
}
|
|
2785
|
+
resumoVinculosPorTask.set(chaveTask, resumo);
|
|
2786
|
+
}
|
|
1578
2787
|
}
|
|
1579
2788
|
}
|
|
1580
|
-
for (const
|
|
1581
|
-
const
|
|
1582
|
-
const
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
},
|
|
1603
|
-
authz: {
|
|
1604
|
-
explicita: false,
|
|
1605
|
-
papeis: [],
|
|
1606
|
-
escopos: [],
|
|
1607
|
-
},
|
|
1608
|
-
dados: {
|
|
1609
|
-
explicita: false,
|
|
1610
|
-
campos: [],
|
|
1611
|
-
},
|
|
1612
|
-
audit: {
|
|
1613
|
-
explicita: false,
|
|
1614
|
-
},
|
|
1615
|
-
segredos: {
|
|
1616
|
-
explicita: false,
|
|
1617
|
-
itens: [],
|
|
1618
|
-
},
|
|
1619
|
-
forbidden: {
|
|
1620
|
-
explicita: false,
|
|
1621
|
-
regras: [],
|
|
1622
|
-
},
|
|
1623
|
-
guarantees: [],
|
|
1624
|
-
garantiasEstruturadas: [],
|
|
1625
|
-
errors: {},
|
|
1626
|
-
errosDetalhados: [],
|
|
1627
|
-
perfilCompatibilidade: "interno",
|
|
1628
|
-
resumoAgente: {
|
|
1629
|
-
riscos: [],
|
|
1630
|
-
checks: [],
|
|
1631
|
-
entidadesAfetadas: [],
|
|
1632
|
-
superficiesPublicas: [],
|
|
1633
|
-
mutacoesPrevistas: [],
|
|
1634
|
-
},
|
|
1635
|
-
tests: [],
|
|
1636
|
-
}, contexto.fontesLegado);
|
|
1637
|
-
if (!esperadas.length || !route.metodo || !route.caminho) {
|
|
2789
|
+
for (const resumo of tasksResumo) {
|
|
2790
|
+
const chaveTask = `${resumo.modulo}:${resumo.task}`;
|
|
2791
|
+
const task = taskPorChave.get(chaveTask);
|
|
2792
|
+
const guardrails = guardrailsPorTask.get(chaveTask) ?? {
|
|
2793
|
+
publica: false,
|
|
2794
|
+
sensivel: false,
|
|
2795
|
+
auth: false,
|
|
2796
|
+
authz: false,
|
|
2797
|
+
dados: false,
|
|
2798
|
+
audit: false,
|
|
2799
|
+
segredos: false,
|
|
2800
|
+
forbidden: false,
|
|
2801
|
+
dadosSensiveis: false,
|
|
2802
|
+
efeitoPrivilegiado: false,
|
|
2803
|
+
exigeSegredos: false,
|
|
2804
|
+
};
|
|
2805
|
+
const resumoVinculos = resumoVinculosPorTask.get(chaveTask) ?? {
|
|
2806
|
+
validos: 0,
|
|
2807
|
+
quebrados: 0,
|
|
2808
|
+
arquivos: new Set(),
|
|
2809
|
+
};
|
|
2810
|
+
if (!task) {
|
|
1638
2811
|
continue;
|
|
1639
2812
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
2813
|
+
resumo.confiancaVinculo = calcularConfiancaTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados);
|
|
2814
|
+
resumo.riscoOperacional = calcularRiscoOperacional(task);
|
|
2815
|
+
resumo.lacunas = resumirLacunasTask(task, resumo.semImplementacao, resumo.implsQuebrados, resumoVinculos.quebrados, guardrails);
|
|
2816
|
+
resumo.scoreSemantico = calcularScoreTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados, resumo.semImplementacao);
|
|
2817
|
+
resumo.arquivosProvaveisEditar = [...new Set([
|
|
2818
|
+
...resumo.arquivosReferenciados,
|
|
2819
|
+
...resumo.candidatosImpl.map((candidato) => candidato.arquivo),
|
|
2820
|
+
...resumoVinculos.arquivos,
|
|
2821
|
+
])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2822
|
+
resumo.checksSugeridos = [...new Set([
|
|
2823
|
+
...task.resumoAgente.checks,
|
|
2824
|
+
resumo.riscoOperacional !== "baixo" ? "revisar efeitos operacionais" : "",
|
|
2825
|
+
resumo.lacunas.includes("vinculo_quebrado") ? "corrigir vinculos rastreaveis" : "",
|
|
2826
|
+
resumo.lacunas.some((lacuna) => ["superficie_publica_sem_execucao", "execucao_critica_sem_bloco", "rastreabilidade_fraca"].includes(lacuna))
|
|
2827
|
+
? "endurecer execucao e rastreabilidade para producao"
|
|
2828
|
+
: "",
|
|
2829
|
+
resumo.lacunas.some((lacuna) => ["auth_ausente", "authz_frouxa", "dados_nao_classificados", "audit_ausente", "segredo_sem_governanca", "proibicoes_ausentes"].includes(lacuna))
|
|
2830
|
+
? "explicitar contratos de seguranca semantica"
|
|
2831
|
+
: "",
|
|
2832
|
+
].filter(Boolean))];
|
|
2833
|
+
if (resumo.lacunas.includes("superficie_publica_sem_execucao")) {
|
|
1656
2834
|
diagnosticos.push({
|
|
1657
|
-
tipo: "
|
|
1658
|
-
modulo:
|
|
1659
|
-
|
|
1660
|
-
mensagem:
|
|
2835
|
+
tipo: "seguranca_frouxa",
|
|
2836
|
+
modulo: resumo.modulo,
|
|
2837
|
+
task: resumo.task,
|
|
2838
|
+
mensagem: `Task "${resumo.task}" alimenta superficie publica, mas ainda depende de execucao implicita.`,
|
|
1661
2839
|
});
|
|
1662
2840
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
valor: itemVinculo.vinculo.valor,
|
|
1671
|
-
status: "nao_encontrado",
|
|
1672
|
-
confianca: "baixa",
|
|
1673
|
-
};
|
|
1674
|
-
const arquivoDeclarado = itemVinculo.vinculo.arquivo ?? (itemVinculo.vinculo.tipo === "arquivo" ? itemVinculo.vinculo.valor : undefined);
|
|
1675
|
-
const simboloDeclarado = itemVinculo.vinculo.simbolo ?? (itemVinculo.vinculo.tipo === "simbolo" ? itemVinculo.vinculo.valor : undefined);
|
|
1676
|
-
const recursoDeclarado = itemVinculo.vinculo.recurso ?? (["recurso", "tabela", "fila", "cache", "storage"].includes(itemVinculo.vinculo.tipo) ? itemVinculo.vinculo.valor : undefined);
|
|
1677
|
-
const superficieDeclarada = itemVinculo.vinculo.superficie ?? (["superficie", "rota", "worker", "cron", "webhook", "evento", "policy", "fila", "cache", "storage"].includes(itemVinculo.vinculo.tipo) ? itemVinculo.vinculo.valor : undefined);
|
|
1678
|
-
if (simboloDeclarado) {
|
|
1679
|
-
const resolucaoSimbolo = escolherSimboloPorVinculo(todosSimbolos, mapaImpl, simboloDeclarado);
|
|
1680
|
-
registro.status = resolucaoSimbolo.status;
|
|
1681
|
-
registro.confianca = resolucaoSimbolo.confianca;
|
|
1682
|
-
registro.arquivo = resolucaoSimbolo.simbolo?.arquivo;
|
|
1683
|
-
registro.simbolo = resolucaoSimbolo.simbolo?.simbolo;
|
|
1684
|
-
}
|
|
1685
|
-
else if (arquivoDeclarado) {
|
|
1686
|
-
const resolucaoArquivo = escolherArquivoPorVinculo(todosArquivosConhecidos, arquivoDeclarado);
|
|
1687
|
-
registro.status = resolucaoArquivo.status;
|
|
1688
|
-
registro.confianca = resolucaoArquivo.confianca;
|
|
1689
|
-
registro.arquivo = resolucaoArquivo.arquivo;
|
|
1690
|
-
}
|
|
1691
|
-
else if (recursoDeclarado) {
|
|
1692
|
-
const recurso = mapaRecursos.get(recursoDeclarado);
|
|
1693
|
-
if (recurso) {
|
|
1694
|
-
registro.status = "resolvido";
|
|
1695
|
-
registro.confianca = "alta";
|
|
1696
|
-
registro.arquivo = recurso.arquivo;
|
|
1697
|
-
}
|
|
2841
|
+
if (resumo.lacunas.includes("execucao_critica_sem_bloco")) {
|
|
2842
|
+
diagnosticos.push({
|
|
2843
|
+
tipo: "seguranca_frouxa",
|
|
2844
|
+
modulo: resumo.modulo,
|
|
2845
|
+
task: resumo.task,
|
|
2846
|
+
mensagem: `Task "${resumo.task}" opera com risco alto, mas ainda nao declarou execucao explicita.`,
|
|
2847
|
+
});
|
|
1698
2848
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
}
|
|
1707
|
-
else {
|
|
1708
|
-
const resolucaoArquivo = escolherArquivoPorVinculo(todosArquivosConhecidos, superficieDeclarada);
|
|
1709
|
-
registro.status = resolucaoArquivo.status;
|
|
1710
|
-
registro.confianca = resolucaoArquivo.confianca;
|
|
1711
|
-
registro.arquivo = resolucaoArquivo.arquivo;
|
|
1712
|
-
}
|
|
2849
|
+
if (resumo.lacunas.includes("rastreabilidade_fraca")) {
|
|
2850
|
+
diagnosticos.push({
|
|
2851
|
+
tipo: "seguranca_frouxa",
|
|
2852
|
+
modulo: resumo.modulo,
|
|
2853
|
+
task: resumo.task,
|
|
2854
|
+
mensagem: `Task "${resumo.task}" exige producao mais rastreavel, mas ainda nao declara impl nem vinculos.`,
|
|
2855
|
+
});
|
|
1713
2856
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
const superficie = superficiesPorChave.get(itemVinculo.dono);
|
|
1722
|
-
const ancora = superficie
|
|
1723
|
-
? encontrarAncoraSuperficie(ir, superficie, todosSimbolos, mapaImpl, todosArquivosConhecidos)
|
|
1724
|
-
: undefined;
|
|
1725
|
-
if (ancora) {
|
|
1726
|
-
registro.status = "parcial";
|
|
1727
|
-
registro.confianca = ancora.confianca === "alta" ? "media" : ancora.confianca;
|
|
1728
|
-
registro.arquivo = registro.arquivo ?? ancora.arquivo;
|
|
1729
|
-
registro.simbolo = registro.simbolo ?? ancora.simbolo;
|
|
1730
|
-
}
|
|
2857
|
+
if (resumo.lacunas.includes("auth_ausente")) {
|
|
2858
|
+
diagnosticos.push({
|
|
2859
|
+
tipo: "seguranca_frouxa",
|
|
2860
|
+
modulo: resumo.modulo,
|
|
2861
|
+
task: resumo.task,
|
|
2862
|
+
mensagem: `Task "${resumo.task}" chega em superficie publica sem auth explicita em task, route ou superficie associada.`,
|
|
2863
|
+
});
|
|
1731
2864
|
}
|
|
1732
|
-
if (
|
|
1733
|
-
vinculosQuebrados.push(registro);
|
|
2865
|
+
if (resumo.lacunas.includes("authz_frouxa")) {
|
|
1734
2866
|
diagnosticos.push({
|
|
1735
|
-
tipo: "
|
|
1736
|
-
modulo:
|
|
1737
|
-
|
|
1738
|
-
|
|
2867
|
+
tipo: "seguranca_frouxa",
|
|
2868
|
+
modulo: resumo.modulo,
|
|
2869
|
+
task: resumo.task,
|
|
2870
|
+
mensagem: `Task "${resumo.task}" opera com risco ou exposicao, mas ainda nao explicita authz suficiente.`,
|
|
1739
2871
|
});
|
|
1740
2872
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
2873
|
+
if (resumo.lacunas.includes("dados_nao_classificados")) {
|
|
2874
|
+
diagnosticos.push({
|
|
2875
|
+
tipo: "seguranca_frouxa",
|
|
2876
|
+
modulo: resumo.modulo,
|
|
2877
|
+
task: resumo.task,
|
|
2878
|
+
mensagem: `Task "${resumo.task}" ainda nao classifica dados de entrada/saida de forma semantica.`,
|
|
2879
|
+
});
|
|
1743
2880
|
}
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
};
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
resumo.
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2881
|
+
if (resumo.lacunas.includes("audit_ausente")) {
|
|
2882
|
+
diagnosticos.push({
|
|
2883
|
+
tipo: "seguranca_frouxa",
|
|
2884
|
+
modulo: resumo.modulo,
|
|
2885
|
+
task: resumo.task,
|
|
2886
|
+
mensagem: `Task "${resumo.task}" ainda nao declara audit explicita para operacao sensivel ou publica.`,
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
if (resumo.lacunas.includes("segredo_sem_governanca")) {
|
|
2890
|
+
diagnosticos.push({
|
|
2891
|
+
tipo: "seguranca_frouxa",
|
|
2892
|
+
modulo: resumo.modulo,
|
|
2893
|
+
task: resumo.task,
|
|
2894
|
+
mensagem: `Task "${resumo.task}" toca segredo ou credencial sem bloco segredos governando origem, escopo e rotacao.`,
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
if (resumo.lacunas.includes("proibicoes_ausentes")) {
|
|
2898
|
+
diagnosticos.push({
|
|
2899
|
+
tipo: "seguranca_frouxa",
|
|
2900
|
+
modulo: resumo.modulo,
|
|
2901
|
+
task: resumo.task,
|
|
2902
|
+
mensagem: `Task "${resumo.task}" opera com efeito privilegiado ou dado sensivel sem forbidden explicito para conter abuso e vazamento.`,
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
const consumersFiltrados = filtrarConsumerSurfacesPorEscopo([...indexTs.consumerSurfaces, ...indexDart.consumerSurfaces].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
|
|
2907
|
+
|| a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
|
|
2908
|
+
|| a.arquivo.localeCompare(b.arquivo, "pt-BR")), [...new Map([...indexTs.simbolos, ...indexDart.simbolos]
|
|
2909
|
+
.filter((simbolo) => simboloEhBridgeConsumer(simbolo.caminho, simbolo.arquivo))
|
|
2910
|
+
.map((simbolo) => [
|
|
2911
|
+
`${simbolo.caminho}:${simbolo.arquivo}:${simbolo.simbolo}`,
|
|
2912
|
+
{
|
|
2913
|
+
caminho: simbolo.caminho,
|
|
2914
|
+
arquivo: simbolo.arquivo,
|
|
2915
|
+
simbolo: simbolo.simbolo,
|
|
2916
|
+
},
|
|
2917
|
+
])).values()].sort((a, b) => a.caminho.localeCompare(b.caminho, "pt-BR")
|
|
2918
|
+
|| a.arquivo.localeCompare(b.arquivo, "pt-BR")), contexto, configuracaoEscopo);
|
|
2919
|
+
const consumerSurfaces = consumersFiltrados.consumerSurfaces;
|
|
2920
|
+
const consumerBridges = consumersFiltrados.consumerBridges;
|
|
2921
|
+
const appRoutes = [...new Set(consumerSurfaces.map((surface) => surface.rota))]
|
|
2922
|
+
.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
2923
|
+
const consumerFramework = inferirConsumerFrameworkPrincipal(contexto.fontesLegado, consumerSurfaces, consumerBridges);
|
|
2924
|
+
const persistenciaReal = await analisarPersistenciaReal(contexto, mapaRecursos, detalhesPersistencia, opcoesResolvidas);
|
|
2925
|
+
for (const item of persistenciaReal) {
|
|
2926
|
+
if (item.status === "divergente") {
|
|
2927
|
+
diagnosticos.push({
|
|
2928
|
+
tipo: "recurso_divergente",
|
|
2929
|
+
modulo: item.modulo,
|
|
2930
|
+
task: item.task,
|
|
2931
|
+
mensagem: `Persistencia real para "${item.alvo}" ainda nao foi materializada no codigo vivo.`,
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
else if (item.compatibilidade === "invalido") {
|
|
2935
|
+
diagnosticos.push({
|
|
2936
|
+
tipo: "recurso_divergente",
|
|
2937
|
+
modulo: item.modulo,
|
|
2938
|
+
task: item.task,
|
|
2939
|
+
mensagem: `Persistencia real para "${item.alvo}" conflita com a compatibilidade declarada do engine ${item.engine}.`,
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
const payloadBase = {
|
|
2944
|
+
comando: "drift",
|
|
2945
|
+
sucesso: implsQuebrados.length === 0
|
|
2946
|
+
&& rotasDivergentes.length === 0
|
|
2947
|
+
&& recursosDivergentes.length === 0
|
|
2948
|
+
&& vinculosQuebrados.length === 0
|
|
2949
|
+
&& persistenciaReal.every((item) => item.status !== "divergente" && item.compatibilidade !== "invalido"),
|
|
2950
|
+
escopo_aplicado: configuracaoEscopo,
|
|
2951
|
+
consumerFramework,
|
|
2952
|
+
appRoutes,
|
|
2953
|
+
consumerSurfaces,
|
|
2954
|
+
consumerBridges,
|
|
2955
|
+
modulos: contexto.modulosSelecionados.map((item) => ({
|
|
2956
|
+
caminho: item.caminho,
|
|
2957
|
+
modulo: item.resultado.ir?.nome ?? item.resultado.modulo?.nome ?? null,
|
|
2958
|
+
tasks: item.resultado.ir?.tasks.length ?? 0,
|
|
2959
|
+
routes: item.resultado.ir?.routes.length ?? 0,
|
|
2960
|
+
})),
|
|
2961
|
+
tasks: tasksResumo,
|
|
2962
|
+
impls_validos: implsValidos,
|
|
2963
|
+
impls_quebrados: implsQuebrados,
|
|
2964
|
+
vinculos_validos: vinculosValidos,
|
|
2965
|
+
vinculos_quebrados: vinculosQuebrados,
|
|
2966
|
+
rotas_divergentes: rotasDivergentes,
|
|
2967
|
+
recursos_validos: recursosValidos,
|
|
2968
|
+
recursos_divergentes: recursosDivergentes,
|
|
2969
|
+
persistencia_real: persistenciaReal,
|
|
2970
|
+
diagnosticos,
|
|
2971
|
+
resumo_operacional: {
|
|
2972
|
+
scoreMedio: 0,
|
|
2973
|
+
confiancaGeral: "baixa",
|
|
2974
|
+
riscosPrincipais: [],
|
|
2975
|
+
oQueTocar: [],
|
|
2976
|
+
oQueValidar: [],
|
|
2977
|
+
oQueEstaFrouxo: [],
|
|
2978
|
+
oQueFoiInferido: [],
|
|
2979
|
+
},
|
|
1779
2980
|
};
|
|
1780
|
-
const
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2981
|
+
const resumoOperacional = resumirOperacional(payloadBase);
|
|
2982
|
+
return {
|
|
2983
|
+
...payloadBase,
|
|
2984
|
+
resumo_operacional: resumoOperacional,
|
|
1784
2985
|
};
|
|
1785
|
-
|
|
1786
|
-
|
|
2986
|
+
}
|
|
2987
|
+
finally {
|
|
2988
|
+
diretoriosIgnoradosAtivos = diretoriosIgnoradosAnteriores;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
const EXTENSOES_BUSCA_IMPACTO = [
|
|
2992
|
+
".sema",
|
|
2993
|
+
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
2994
|
+
".py", ".dart", ".cs", ".java", ".go", ".rs", ".cpp", ".cc", ".cxx", ".hpp", ".h",
|
|
2995
|
+
".sql", ".psql", ".ddl", ".prisma", ".json",
|
|
2996
|
+
];
|
|
2997
|
+
function construirVariantesSemanticas(valor) {
|
|
2998
|
+
const bruto = valor.trim();
|
|
2999
|
+
const partes = paraIdentificadorModulo(valor).split("_").filter(Boolean);
|
|
3000
|
+
if (!bruto && partes.length === 0) {
|
|
3001
|
+
return [];
|
|
3002
|
+
}
|
|
3003
|
+
const camel = partes.length > 0
|
|
3004
|
+
? `${partes[0]}${partes.slice(1).map((item) => item[0]?.toUpperCase() + item.slice(1)).join("")}`
|
|
3005
|
+
: bruto;
|
|
3006
|
+
const pascal = partes.length > 0
|
|
3007
|
+
? partes.map((item) => item[0]?.toUpperCase() + item.slice(1)).join("")
|
|
3008
|
+
: bruto;
|
|
3009
|
+
return [...new Set([
|
|
3010
|
+
bruto,
|
|
3011
|
+
partes.join("_"),
|
|
3012
|
+
partes.join("-"),
|
|
3013
|
+
partes.join("."),
|
|
3014
|
+
camel,
|
|
3015
|
+
pascal,
|
|
3016
|
+
].filter(Boolean))];
|
|
3017
|
+
}
|
|
3018
|
+
function classificarArquivoImpacto(arquivo) {
|
|
3019
|
+
const normalizado = normalizarFragmentoArquivo(arquivo);
|
|
3020
|
+
if (normalizado.endsWith(".sema")) {
|
|
3021
|
+
return "contrato";
|
|
3022
|
+
}
|
|
3023
|
+
if (/\.(sql|psql|ddl|prisma)$/i.test(normalizado) || /(?:^|\/)(?:db|database|migrations?|schemas?)\//i.test(normalizado)) {
|
|
3024
|
+
return "persistencia";
|
|
3025
|
+
}
|
|
3026
|
+
if (/(?:^|\/)(?:repositorio|repositorios|repository|repositories|repo|dao|store)\//i.test(normalizado) || /(repository|repositorio|dao|store)/i.test(path.basename(normalizado))) {
|
|
3027
|
+
return "repositorio";
|
|
3028
|
+
}
|
|
3029
|
+
if (/(?:^|\/)(?:routes?|controllers?|api)\//i.test(normalizado) || /(controller|route)/i.test(path.basename(normalizado))) {
|
|
3030
|
+
return "rota";
|
|
3031
|
+
}
|
|
3032
|
+
if (/(?:^|\/)(?:workers?|jobs?|queues?|cron)\//i.test(normalizado) || /(worker|job|queue|cron)/i.test(path.basename(normalizado))) {
|
|
3033
|
+
return "worker";
|
|
3034
|
+
}
|
|
3035
|
+
if (/(?:^|\/)(?:pages|screens|components|views|app)\//i.test(normalizado)) {
|
|
3036
|
+
return "ui";
|
|
3037
|
+
}
|
|
3038
|
+
if (/(?:^|\/)(?:tests?|specs?|__tests__)\//i.test(normalizado) || /\.(spec|test)\./i.test(normalizado)) {
|
|
3039
|
+
return "teste";
|
|
3040
|
+
}
|
|
3041
|
+
return "codigo";
|
|
3042
|
+
}
|
|
3043
|
+
function prioridadeArquivoImpacto(tipo) {
|
|
3044
|
+
switch (tipo) {
|
|
3045
|
+
case "contrato":
|
|
3046
|
+
case "persistencia":
|
|
3047
|
+
case "repositorio":
|
|
3048
|
+
case "rota":
|
|
3049
|
+
return "alta";
|
|
3050
|
+
case "worker":
|
|
3051
|
+
case "codigo":
|
|
3052
|
+
return "media";
|
|
3053
|
+
default:
|
|
3054
|
+
return "baixa";
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
function textoIrCombinaTermos(texto, termos) {
|
|
3058
|
+
return textoCombinaEscopo(texto, termos);
|
|
3059
|
+
}
|
|
3060
|
+
function registrarArquivoImpactado(mapa, arquivo, linhas, motivos) {
|
|
3061
|
+
const tipo = classificarArquivoImpacto(arquivo);
|
|
3062
|
+
const atual = mapa.get(arquivo);
|
|
3063
|
+
if (atual) {
|
|
3064
|
+
atual.linhas = [...new Set([...atual.linhas, ...linhas])].sort((a, b) => a - b);
|
|
3065
|
+
atual.motivos = [...new Set([...atual.motivos, ...motivos])];
|
|
3066
|
+
if (prioridadeArquivoImpacto(tipo) === "alta") {
|
|
3067
|
+
atual.prioridade = "alta";
|
|
1787
3068
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
resumo.lacunas = resumirLacunasTask(task, resumo.semImplementacao, resumo.implsQuebrados, resumoVinculos.quebrados, guardrails);
|
|
1791
|
-
resumo.scoreSemantico = calcularScoreTask(task, resumo.implsValidos, resumo.implsQuebrados, resumoVinculos.validos, resumoVinculos.quebrados, resumo.semImplementacao);
|
|
1792
|
-
resumo.arquivosProvaveisEditar = [...new Set([
|
|
1793
|
-
...resumo.arquivosReferenciados,
|
|
1794
|
-
...resumo.candidatosImpl.map((candidato) => candidato.arquivo),
|
|
1795
|
-
...resumoVinculos.arquivos,
|
|
1796
|
-
])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
1797
|
-
resumo.checksSugeridos = [...new Set([
|
|
1798
|
-
...task.resumoAgente.checks,
|
|
1799
|
-
resumo.riscoOperacional !== "baixo" ? "revisar efeitos operacionais" : "",
|
|
1800
|
-
resumo.lacunas.includes("vinculo_quebrado") ? "corrigir vinculos rastreaveis" : "",
|
|
1801
|
-
resumo.lacunas.some((lacuna) => ["superficie_publica_sem_execucao", "execucao_critica_sem_bloco", "rastreabilidade_fraca"].includes(lacuna))
|
|
1802
|
-
? "endurecer execucao e rastreabilidade para producao"
|
|
1803
|
-
: "",
|
|
1804
|
-
resumo.lacunas.some((lacuna) => ["auth_ausente", "authz_frouxa", "dados_nao_classificados", "audit_ausente", "segredo_sem_governanca", "proibicoes_ausentes"].includes(lacuna))
|
|
1805
|
-
? "explicitar contratos de seguranca semantica"
|
|
1806
|
-
: "",
|
|
1807
|
-
].filter(Boolean))];
|
|
1808
|
-
if (resumo.lacunas.includes("superficie_publica_sem_execucao")) {
|
|
1809
|
-
diagnosticos.push({
|
|
1810
|
-
tipo: "seguranca_frouxa",
|
|
1811
|
-
modulo: resumo.modulo,
|
|
1812
|
-
task: resumo.task,
|
|
1813
|
-
mensagem: `Task "${resumo.task}" alimenta superficie publica, mas ainda depende de execucao implicita.`,
|
|
1814
|
-
});
|
|
3069
|
+
else if (prioridadeArquivoImpacto(tipo) === "media" && atual.prioridade === "baixa") {
|
|
3070
|
+
atual.prioridade = "media";
|
|
1815
3071
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
mapa.set(arquivo, {
|
|
3075
|
+
arquivo,
|
|
3076
|
+
tipo,
|
|
3077
|
+
prioridade: prioridadeArquivoImpacto(tipo),
|
|
3078
|
+
linhas: [...new Set(linhas)].sort((a, b) => a - b),
|
|
3079
|
+
motivos: [...new Set(motivos)],
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
async function listarArquivosImpacto(contexto, opcoes) {
|
|
3083
|
+
const opcoesResolvidas = resolverOpcoesDrift(opcoes);
|
|
3084
|
+
const arquivos = new Set(filtrarCaminhosEscopoReal(contexto.arquivosProjeto, contexto, opcoesResolvidas));
|
|
3085
|
+
for (const diretorio of resolverDiretoriosCodigoEscopoReal(contexto, opcoesResolvidas)) {
|
|
3086
|
+
for (const arquivo of await listarArquivosRecursivos(diretorio, EXTENSOES_BUSCA_IMPACTO)) {
|
|
3087
|
+
arquivos.add(arquivo);
|
|
1831
3088
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
3089
|
+
}
|
|
3090
|
+
return [...arquivos].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
3091
|
+
}
|
|
3092
|
+
function extrairLinhasComVariantes(codigo, variantes) {
|
|
3093
|
+
const linhas = [];
|
|
3094
|
+
const texto = codigo.split(/\r?\n/);
|
|
3095
|
+
for (let indice = 0; indice < texto.length; indice += 1) {
|
|
3096
|
+
if (variantes.some((variante) => variante && texto[indice].includes(variante))) {
|
|
3097
|
+
linhas.push(indice + 1);
|
|
1839
3098
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
3099
|
+
}
|
|
3100
|
+
return linhas;
|
|
3101
|
+
}
|
|
3102
|
+
function serializarTaskParaImpacto(task) {
|
|
3103
|
+
return JSON.stringify({
|
|
3104
|
+
nome: task.nome,
|
|
3105
|
+
input: task.input.map((campo) => campo.nome),
|
|
3106
|
+
output: task.output.map((campo) => campo.nome),
|
|
3107
|
+
effects: task.effects,
|
|
3108
|
+
guarantees: task.guarantees,
|
|
3109
|
+
errors: task.errors,
|
|
3110
|
+
resumo: task.resumoAgente,
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
function serializarRouteParaImpacto(route) {
|
|
3114
|
+
return JSON.stringify({
|
|
3115
|
+
nome: route.nome,
|
|
3116
|
+
caminho: route.caminho,
|
|
3117
|
+
metodo: route.metodo,
|
|
3118
|
+
task: route.task,
|
|
3119
|
+
input: route.inputPublico.map((campo) => campo.nome),
|
|
3120
|
+
output: route.outputPublico.map((campo) => campo.nome),
|
|
3121
|
+
});
|
|
3122
|
+
}
|
|
3123
|
+
function serializarSuperficieParaImpacto(superficie) {
|
|
3124
|
+
return JSON.stringify({
|
|
3125
|
+
tipo: superficie.tipo,
|
|
3126
|
+
nome: superficie.nome,
|
|
3127
|
+
task: superficie.task,
|
|
3128
|
+
input: superficie.input.map((campo) => campo.nome),
|
|
3129
|
+
output: superficie.output.map((campo) => campo.nome),
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
function ordenarArquivosImpacto(arquivos) {
|
|
3133
|
+
const ordemPrioridade = { alta: 0, media: 1, baixa: 2 };
|
|
3134
|
+
return [...arquivos].sort((a, b) => ordemPrioridade[a.prioridade] - ordemPrioridade[b.prioridade]
|
|
3135
|
+
|| a.tipo.localeCompare(b.tipo, "pt-BR")
|
|
3136
|
+
|| a.arquivo.localeCompare(b.arquivo, "pt-BR"));
|
|
3137
|
+
}
|
|
3138
|
+
export async function gerarMapaImpactoSemantico(contexto, alvoSemantico, mudancaProposta, opcoes) {
|
|
3139
|
+
const opcoesResolvidas = resolverOpcoesDrift(opcoes);
|
|
3140
|
+
const diretoriosIgnoradosAnteriores = diretoriosIgnoradosAtivos;
|
|
3141
|
+
diretoriosIgnoradosAtivos = resolverDiretoriosIgnoradosAtivos(opcoesResolvidas);
|
|
3142
|
+
try {
|
|
3143
|
+
const drift = await analisarDriftLegado(contexto, opcoesResolvidas);
|
|
3144
|
+
const variantes = construirVariantesSemanticas(alvoSemantico);
|
|
3145
|
+
const termos = [...new Set([...quebrarTermosEscopo(alvoSemantico), ...drift.escopo_aplicado.termosEscopo])];
|
|
3146
|
+
const arquivosImpactados = new Map();
|
|
3147
|
+
const arquivosBusca = await listarArquivosImpacto(contexto, opcoesResolvidas);
|
|
3148
|
+
for (const arquivo of arquivosBusca) {
|
|
3149
|
+
const codigo = await readFile(arquivo, "utf8");
|
|
3150
|
+
const linhas = extrairLinhasComVariantes(codigo, variantes);
|
|
3151
|
+
if (linhas.length > 0) {
|
|
3152
|
+
registrarArquivoImpactado(arquivosImpactados, arquivo, linhas, ["token_semantico_encontrado"]);
|
|
3153
|
+
}
|
|
1847
3154
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
3155
|
+
const tasksAfetadas = new Set();
|
|
3156
|
+
const routesAfetadas = new Set();
|
|
3157
|
+
const superficiesAfetadas = new Set();
|
|
3158
|
+
const persistenciaAfetada = new Set();
|
|
3159
|
+
for (const item of contexto.modulosSelecionados) {
|
|
3160
|
+
const ir = item.resultado.ir;
|
|
3161
|
+
if (!ir) {
|
|
3162
|
+
continue;
|
|
3163
|
+
}
|
|
3164
|
+
for (const task of ir.tasks) {
|
|
3165
|
+
if (textoIrCombinaTermos(serializarTaskParaImpacto(task), termos)) {
|
|
3166
|
+
tasksAfetadas.add(`${ir.nome}.${task.nome}`);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
for (const route of ir.routes) {
|
|
3170
|
+
if (textoIrCombinaTermos(serializarRouteParaImpacto(route), termos)) {
|
|
3171
|
+
routesAfetadas.add(`${ir.nome}.${route.nome}`);
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
for (const superficie of ir.superficies) {
|
|
3175
|
+
if (textoIrCombinaTermos(serializarSuperficieParaImpacto(superficie), termos)) {
|
|
3176
|
+
superficiesAfetadas.add(`${ir.nome}.${superficie.tipo}.${superficie.nome}`);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
1855
3179
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
task: resumo.task,
|
|
1861
|
-
mensagem: `Task "${resumo.task}" ainda nao declara audit explicita para operacao sensivel ou publica.`,
|
|
1862
|
-
});
|
|
3180
|
+
for (const task of drift.tasks.filter((item) => tasksAfetadas.has(`${item.modulo}.${item.task}`))) {
|
|
3181
|
+
for (const arquivo of task.arquivosProvaveisEditar) {
|
|
3182
|
+
registrarArquivoImpactado(arquivosImpactados, arquivo, [], ["arquivo_relacionado_por_drift"]);
|
|
3183
|
+
}
|
|
1863
3184
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
}
|
|
3185
|
+
for (const item of drift.persistencia_real) {
|
|
3186
|
+
if (textoIrCombinaTermos(`${item.alvo} ${item.task} ${item.colunas.join(" ")}`, termos)) {
|
|
3187
|
+
persistenciaAfetada.add(`${item.task}:${item.alvo}`);
|
|
3188
|
+
for (const arquivo of [...item.arquivos, ...item.repositorios]) {
|
|
3189
|
+
registrarArquivoImpactado(arquivosImpactados, arquivo, [], ["persistencia_relacionada"]);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
1871
3192
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
3193
|
+
const contratosAfetados = ordenarArquivosImpacto([...arquivosImpactados.values()].filter((arquivo) => arquivo.tipo === "contrato")).map((arquivo) => arquivo.arquivo);
|
|
3194
|
+
return {
|
|
3195
|
+
comando: "impacto",
|
|
3196
|
+
sucesso: arquivosImpactados.size > 0 || tasksAfetadas.size > 0 || persistenciaAfetada.size > 0,
|
|
3197
|
+
escopo: drift.escopo_aplicado.escopo,
|
|
3198
|
+
alvoSemantico,
|
|
3199
|
+
mudancaProposta,
|
|
3200
|
+
contratosAfetados,
|
|
3201
|
+
tasksAfetadas: [...tasksAfetadas].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
3202
|
+
routesAfetadas: [...routesAfetadas].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
3203
|
+
superficiesAfetadas: [...superficiesAfetadas].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
3204
|
+
persistenciaAfetada: [...persistenciaAfetada].sort((a, b) => a.localeCompare(b, "pt-BR")),
|
|
3205
|
+
arquivos: ordenarArquivosImpacto([...arquivosImpactados.values()]),
|
|
3206
|
+
ordemOperacional: [
|
|
3207
|
+
"Atualizar contrato .sema e revisar garantias publicas primeiro.",
|
|
3208
|
+
"Ajustar persistencia e repositorios concretos antes de materializacao externa.",
|
|
3209
|
+
"Revisar rotas, workers e bridges depois que o contrato e o storage estiverem coerentes.",
|
|
3210
|
+
"Fechar com UI/consumidores e testes alinhados ao payload final.",
|
|
3211
|
+
],
|
|
3212
|
+
validacoes: [
|
|
3213
|
+
"Rodar sema validar no contrato alterado.",
|
|
3214
|
+
"Rodar sema drift com o mesmo escopo apos a mudanca.",
|
|
3215
|
+
"Revalidar testes de payload, persistencia e superficies publicas.",
|
|
3216
|
+
],
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
finally {
|
|
3220
|
+
diretoriosIgnoradosAtivos = diretoriosIgnoradosAnteriores;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
export async function assistirRenomeacaoSemantica(contexto, nomeAtual, nomeNovo, opcoes) {
|
|
3224
|
+
const impacto = await gerarMapaImpactoSemantico(contexto, nomeAtual, `renomear ${nomeAtual} para ${nomeNovo}`, opcoes);
|
|
3225
|
+
const variantesAntigas = construirVariantesSemanticas(nomeAtual);
|
|
3226
|
+
const variantesNovas = construirVariantesSemanticas(nomeNovo);
|
|
3227
|
+
const mapaSubstituicao = new Map();
|
|
3228
|
+
variantesAntigas.forEach((antiga, indice) => {
|
|
3229
|
+
mapaSubstituicao.set(antiga, variantesNovas[indice] ?? nomeNovo);
|
|
3230
|
+
});
|
|
3231
|
+
const sugestoes = [];
|
|
3232
|
+
for (const arquivo of impacto.arquivos) {
|
|
3233
|
+
const codigo = await readFile(arquivo.arquivo, "utf8");
|
|
3234
|
+
const linhas = codigo.split(/\r?\n/);
|
|
3235
|
+
for (let indice = 0; indice < linhas.length; indice += 1) {
|
|
3236
|
+
const linha = linhas[indice];
|
|
3237
|
+
for (const antiga of variantesAntigas) {
|
|
3238
|
+
if (!antiga || !linha.includes(antiga)) {
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
sugestoes.push({
|
|
3242
|
+
arquivo: arquivo.arquivo,
|
|
3243
|
+
linha: indice + 1,
|
|
3244
|
+
atual: antiga,
|
|
3245
|
+
sugerido: mapaSubstituicao.get(antiga) ?? nomeNovo,
|
|
3246
|
+
contexto: linha.trim().slice(0, 180),
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
1879
3249
|
}
|
|
1880
3250
|
}
|
|
1881
|
-
const consumerSurfaces = [...indexTs.consumerSurfaces, ...indexDart.consumerSurfaces].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
|
|
1882
|
-
|| a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
|
|
1883
|
-
|| a.arquivo.localeCompare(b.arquivo, "pt-BR"));
|
|
1884
|
-
const consumerBridges = [...new Map([...indexTs.simbolos, ...indexDart.simbolos]
|
|
1885
|
-
.filter((simbolo) => simboloEhBridgeConsumer(simbolo.caminho, simbolo.arquivo))
|
|
1886
|
-
.map((simbolo) => [
|
|
1887
|
-
`${simbolo.caminho}:${simbolo.arquivo}:${simbolo.simbolo}`,
|
|
1888
|
-
{
|
|
1889
|
-
caminho: simbolo.caminho,
|
|
1890
|
-
arquivo: simbolo.arquivo,
|
|
1891
|
-
simbolo: simbolo.simbolo,
|
|
1892
|
-
},
|
|
1893
|
-
])).values()].sort((a, b) => a.caminho.localeCompare(b.caminho, "pt-BR")
|
|
1894
|
-
|| a.arquivo.localeCompare(b.arquivo, "pt-BR"));
|
|
1895
|
-
const appRoutes = [...new Set(consumerSurfaces.map((surface) => surface.rota))]
|
|
1896
|
-
.sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
1897
|
-
const consumerFramework = inferirConsumerFrameworkPrincipal(contexto.fontesLegado, consumerSurfaces, consumerBridges);
|
|
1898
|
-
const payloadBase = {
|
|
1899
|
-
comando: "drift",
|
|
1900
|
-
sucesso: implsQuebrados.length === 0 && rotasDivergentes.length === 0 && recursosDivergentes.length === 0 && vinculosQuebrados.length === 0,
|
|
1901
|
-
consumerFramework,
|
|
1902
|
-
appRoutes,
|
|
1903
|
-
consumerSurfaces,
|
|
1904
|
-
consumerBridges,
|
|
1905
|
-
modulos: contexto.modulosSelecionados.map((item) => ({
|
|
1906
|
-
caminho: item.caminho,
|
|
1907
|
-
modulo: item.resultado.ir?.nome ?? item.resultado.modulo?.nome ?? null,
|
|
1908
|
-
tasks: item.resultado.ir?.tasks.length ?? 0,
|
|
1909
|
-
routes: item.resultado.ir?.routes.length ?? 0,
|
|
1910
|
-
})),
|
|
1911
|
-
tasks: tasksResumo,
|
|
1912
|
-
impls_validos: implsValidos,
|
|
1913
|
-
impls_quebrados: implsQuebrados,
|
|
1914
|
-
vinculos_validos: vinculosValidos,
|
|
1915
|
-
vinculos_quebrados: vinculosQuebrados,
|
|
1916
|
-
rotas_divergentes: rotasDivergentes,
|
|
1917
|
-
recursos_validos: recursosValidos,
|
|
1918
|
-
recursos_divergentes: recursosDivergentes,
|
|
1919
|
-
diagnosticos,
|
|
1920
|
-
resumo_operacional: {
|
|
1921
|
-
scoreMedio: 0,
|
|
1922
|
-
confiancaGeral: "baixa",
|
|
1923
|
-
riscosPrincipais: [],
|
|
1924
|
-
oQueTocar: [],
|
|
1925
|
-
oQueValidar: [],
|
|
1926
|
-
oQueEstaFrouxo: [],
|
|
1927
|
-
oQueFoiInferido: [],
|
|
1928
|
-
},
|
|
1929
|
-
};
|
|
1930
|
-
const resumoOperacional = resumirOperacional(payloadBase);
|
|
1931
3251
|
return {
|
|
1932
|
-
|
|
1933
|
-
|
|
3252
|
+
comando: "renomear-semantico",
|
|
3253
|
+
sucesso: sugestoes.length > 0,
|
|
3254
|
+
escopo: impacto.escopo,
|
|
3255
|
+
de: nomeAtual,
|
|
3256
|
+
para: nomeNovo,
|
|
3257
|
+
arquivos: impacto.arquivos,
|
|
3258
|
+
sugestoes,
|
|
3259
|
+
ordemOperacional: [
|
|
3260
|
+
"Renomear primeiro no contrato .sema e nos campos publicos derivados.",
|
|
3261
|
+
"Ajustar repositorios, payloads e bridges que materializam o nome antigo.",
|
|
3262
|
+
"Rodar sema drift e revisar sugestoes restantes antes de fechar a troca.",
|
|
3263
|
+
],
|
|
3264
|
+
validacoes: [
|
|
3265
|
+
"Rodar sema validar no contrato renomeado.",
|
|
3266
|
+
"Rodar sema drift para confirmar que payload e superficie nao ficaram misturados.",
|
|
3267
|
+
"Reexecutar testes e checar snapshots ou fixtures afetados.",
|
|
3268
|
+
],
|
|
1934
3269
|
};
|
|
1935
3270
|
}
|
|
1936
3271
|
//# sourceMappingURL=drift.js.map
|