@semacode/cli 0.9.0 → 1.1.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.
Files changed (46) hide show
  1. package/AGENTS.md +50 -0
  2. package/README.md +24 -3
  3. package/SEMA_BRIEF.curto.txt +9 -0
  4. package/SEMA_BRIEF.md +49 -0
  5. package/SEMA_BRIEF.micro.txt +7 -0
  6. package/SEMA_INDEX.json +501 -0
  7. package/dist/drift.d.ts +15 -0
  8. package/dist/drift.js +496 -5
  9. package/dist/drift.js.map +1 -1
  10. package/dist/importador.d.ts +1 -1
  11. package/dist/importador.js +681 -3
  12. package/dist/importador.js.map +1 -1
  13. package/dist/index.js +1578 -123
  14. package/dist/index.js.map +1 -1
  15. package/dist/projeto.js +49 -1
  16. package/dist/projeto.js.map +1 -1
  17. package/dist/tipos.d.ts +1 -1
  18. package/docs/AGENT_STARTER.md +40 -8
  19. package/docs/como-ensinar-a-sema-para-ia.md +17 -11
  20. package/docs/fluxo-pratico-ia-sema.md +42 -38
  21. package/docs/instalacao-e-primeiro-uso.md +196 -0
  22. package/docs/integracao-com-ia.md +228 -0
  23. package/docs/pagamento-ponta-a-ponta.md +155 -0
  24. package/docs/prompt-base-ia-sema.md +10 -3
  25. package/docs/sintaxe.md +267 -0
  26. package/exemplos/automacao.sema +107 -0
  27. package/exemplos/cadastro_usuario.sema +54 -0
  28. package/exemplos/calculadora.sema +78 -0
  29. package/exemplos/crud_simples.sema +89 -0
  30. package/exemplos/operacao_estrategia.sema +402 -0
  31. package/exemplos/pagamento.sema +222 -0
  32. package/exemplos/pagamento_dominio.sema +35 -0
  33. package/exemplos/testes_embutidos.sema +45 -0
  34. package/exemplos/tratamento_erro.sema +157 -0
  35. package/llms-full.txt +34 -0
  36. package/llms.txt +17 -0
  37. package/node_modules/@sema/gerador-dart/package.json +1 -1
  38. package/node_modules/@sema/gerador-python/dist/index.js +92 -10
  39. package/node_modules/@sema/gerador-python/dist/index.js.map +1 -1
  40. package/node_modules/@sema/gerador-python/package.json +1 -1
  41. package/node_modules/@sema/gerador-typescript/package.json +1 -1
  42. package/node_modules/@sema/nucleo/package.json +1 -1
  43. package/node_modules/@sema/padroes/dist/index.js +47 -1
  44. package/node_modules/@sema/padroes/dist/index.js.map +1 -1
  45. package/node_modules/@sema/padroes/package.json +1 -1
  46. package/package.json +15 -7
package/dist/drift.d.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import type { NivelConfiancaSemantica, NivelRiscoSemantico } from "@sema/nucleo";
2
2
  import type { ContextoProjetoCarregado } from "./projeto.js";
3
+ type ConsumerFramework = "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer";
4
+ interface RegistroConsumerSurfaceDrift {
5
+ rota: string;
6
+ arquivo: string;
7
+ tipoArquivo: string;
8
+ }
9
+ interface RegistroConsumerBridgeDrift {
10
+ caminho: string;
11
+ arquivo: string;
12
+ simbolo: string;
13
+ }
3
14
  export interface DiagnosticoDrift {
4
15
  tipo: "impl_quebrado" | "task_sem_impl" | "rota_divergente" | "recurso_divergente" | "vinculo_quebrado";
5
16
  modulo: string;
@@ -74,6 +85,10 @@ interface RegistroVinculoDrift {
74
85
  export interface ResultadoDrift {
75
86
  comando: "drift";
76
87
  sucesso: boolean;
88
+ consumerFramework: ConsumerFramework | null;
89
+ appRoutes: string[];
90
+ consumerSurfaces: RegistroConsumerSurfaceDrift[];
91
+ consumerBridges: RegistroConsumerBridgeDrift[];
77
92
  modulos: Array<{
78
93
  caminho: string;
79
94
  modulo: string | null;
package/dist/drift.js CHANGED
@@ -291,6 +291,335 @@ function registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, nome, no
291
291
  });
292
292
  }
293
293
  }
294
+ function normalizarRelacaoConsumer(relacaoArquivo) {
295
+ return relacaoArquivo.replace(/\\/g, "/");
296
+ }
297
+ function normalizarSegmentoRotaConsumer(segmento) {
298
+ const opcionalCatchAll = segmento.match(/^\[\[\.\.\.([A-Za-z_]\w*)\]\]$/);
299
+ if (opcionalCatchAll) {
300
+ return `{${opcionalCatchAll[1]}}`;
301
+ }
302
+ const catchAll = segmento.match(/^\[\.\.\.([A-Za-z_]\w*)\]$/);
303
+ if (catchAll) {
304
+ return `{${catchAll[1]}}`;
305
+ }
306
+ const dinamico = segmento.match(/^\[([A-Za-z_]\w*)\]$/);
307
+ if (dinamico) {
308
+ return `{${dinamico[1]}}`;
309
+ }
310
+ return segmento;
311
+ }
312
+ function montarRotaConsumer(partes) {
313
+ const filtradas = partes
314
+ .filter((segmento) => segmento && segmento !== "index" && !/^\(.*\)$/.test(segmento) && !segmento.startsWith("@"))
315
+ .map(normalizarSegmentoRotaConsumer);
316
+ return filtradas.length > 0 ? `/${filtradas.join("/")}`.replace(/\/+/g, "/") : "/";
317
+ }
318
+ function arquivoEhBridgeNextJsConsumer(relacaoArquivo) {
319
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
320
+ return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
321
+ }
322
+ function arquivoEhBridgeReactViteConsumer(relacaoArquivo) {
323
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
324
+ return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
325
+ }
326
+ function arquivoEhBridgeAngularConsumer(relacaoArquivo) {
327
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
328
+ return /(?:^|\/)(?:src\/)?app\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|js)$/i.test(relacao);
329
+ }
330
+ function arquivoEhSuperficieNextJsConsumer(relacaoArquivo) {
331
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
332
+ return /(?:^|\/)(?:src\/)?app\/(?:(?!api\/).)*?(?:page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
333
+ }
334
+ function arquivoEhSuperficieReactViteConsumer(relacaoArquivo) {
335
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
336
+ return /^(?:src\/)?pages\/.+\.(?:ts|tsx|js|jsx)$/i.test(relacao)
337
+ || /^(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao);
338
+ }
339
+ function arquivoEhRotasReactViteConsumer(relacaoArquivo, codigo) {
340
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
341
+ return /(?:^|\/)(?:src\/)?(?:app\/)?(?:router|routes)\.(?:ts|tsx|js|jsx)$/i.test(relacao)
342
+ || /from\s+["']react-router-dom["']|createBrowserRouter|RouterProvider|useRoutes\s*\(|<Routes\b|<Route\b/.test(codigo ?? "");
343
+ }
344
+ function arquivoEhRotasAngularConsumer(relacaoArquivo) {
345
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
346
+ return /(?:^|\/)(?:src\/)?app(?:\/.+)?\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
347
+ }
348
+ function arquivoEhRotasAngularConsumerRaiz(relacaoArquivo) {
349
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
350
+ return /(?:^|\/)(?:src\/)?app\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
351
+ }
352
+ function arquivoEhBridgeFlutterConsumer(relacaoArquivo) {
353
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
354
+ return /(?:^|\/)(?:lib\/)?(?:sema_consumer_bridge|api\/sema_contract_bridge|sema\/.+)\.dart$/i.test(relacao);
355
+ }
356
+ function arquivoEhSuperficieFlutterConsumer(relacaoArquivo) {
357
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
358
+ return /(?:^|\/)(?:lib\/)?(?:screens|pages)\/.+\.dart$/i.test(relacao)
359
+ || /(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao);
360
+ }
361
+ function arquivoEhRotasFlutterConsumer(relacaoArquivo, codigo) {
362
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
363
+ return /(?:^|\/)(?:lib\/)?(?:router|app_router|routes)\.dart$/i.test(relacao)
364
+ || /MaterialApp(?:\.router)?\s*\(|CupertinoApp(?:\.router)?\s*\(|GoRouter\s*\(/.test(codigo ?? "");
365
+ }
366
+ function inferirRotaNextJsConsumer(relacaoArquivo) {
367
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
368
+ const segmentos = relacao.split("/");
369
+ const indiceSrcApp = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "app");
370
+ const indiceApp = segmentos.findIndex((segmento) => segmento === "app");
371
+ const inicioApp = indiceSrcApp >= 0 ? indiceSrcApp + 2 : indiceApp >= 0 ? indiceApp + 1 : -1;
372
+ if (inicioApp < 0) {
373
+ return undefined;
374
+ }
375
+ const arquivoFinal = segmentos.at(-1) ?? "";
376
+ const tipoArquivo = arquivoFinal.match(/^(page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/)?.[1];
377
+ if (!tipoArquivo) {
378
+ return undefined;
379
+ }
380
+ const caminhoAteArquivo = segmentos.slice(inicioApp, -1);
381
+ if (caminhoAteArquivo[0] === "api") {
382
+ return undefined;
383
+ }
384
+ return {
385
+ rota: montarRotaConsumer(caminhoAteArquivo),
386
+ arquivo: relacaoArquivo,
387
+ tipoArquivo,
388
+ };
389
+ }
390
+ function inferirRotaReactViteConsumer(relacaoArquivo) {
391
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
392
+ if (/(?:^|\/)(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao)) {
393
+ return {
394
+ rota: "/",
395
+ arquivo: relacaoArquivo,
396
+ tipoArquivo: "app",
397
+ };
398
+ }
399
+ const segmentos = relacao.split("/");
400
+ const indiceSrcPages = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "pages");
401
+ const indicePages = segmentos.findIndex((segmento) => segmento === "pages");
402
+ const inicioPages = indiceSrcPages >= 0 ? indiceSrcPages + 2 : indicePages >= 0 ? indicePages + 1 : -1;
403
+ if (inicioPages < 0) {
404
+ return undefined;
405
+ }
406
+ const arquivoFinal = segmentos.at(-1) ?? "";
407
+ const nomeBase = arquivoFinal.replace(/\.(?:ts|tsx|js|jsx)$/i, "");
408
+ return {
409
+ rota: montarRotaConsumer([...segmentos.slice(inicioPages, -1), nomeBase]),
410
+ arquivo: relacaoArquivo,
411
+ tipoArquivo: "page",
412
+ };
413
+ }
414
+ function inferirRotaFlutterConsumer(relacaoArquivo) {
415
+ const relacao = normalizarRelacaoConsumer(relacaoArquivo);
416
+ if (!arquivoEhSuperficieFlutterConsumer(relacao)) {
417
+ return undefined;
418
+ }
419
+ if (/(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao)) {
420
+ return {
421
+ rota: "/",
422
+ arquivo: relacaoArquivo,
423
+ tipoArquivo: "app",
424
+ };
425
+ }
426
+ const segmentos = relacao.split("/");
427
+ const indiceLibScreens = segmentos.findIndex((segmento, indice) => segmento === "lib" && ["screens", "pages"].includes(segmentos[indice + 1] ?? ""));
428
+ const indiceScreens = segmentos.findIndex((segmento) => segmento === "screens" || segmento === "pages");
429
+ const inicio = indiceLibScreens >= 0 ? indiceLibScreens + 2 : indiceScreens >= 0 ? indiceScreens + 1 : -1;
430
+ if (inicio < 0) {
431
+ return undefined;
432
+ }
433
+ const arquivoFinal = segmentos.at(-1) ?? "";
434
+ const nomeBase = arquivoFinal
435
+ .replace(/\.(?:dart)$/i, "")
436
+ .replace(/_(screen|page)$/i, "");
437
+ return {
438
+ rota: montarRotaConsumer([...segmentos.slice(inicio, -1), nomeBase]),
439
+ arquivo: relacaoArquivo,
440
+ tipoArquivo: "screen",
441
+ };
442
+ }
443
+ function normalizarRotaDeclaradaConsumer(caminhoCru, prefixo = "/") {
444
+ const partesPrefixo = prefixo.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
445
+ const partesCaminho = (caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
446
+ return montarRotaConsumer([...partesPrefixo, ...partesCaminho]);
447
+ }
448
+ function resolverImportRelativoConsumer(relacaoArquivoBase, especificador) {
449
+ if (!especificador.startsWith(".")) {
450
+ return undefined;
451
+ }
452
+ const baseDir = path.posix.dirname(normalizarRelacaoConsumer(relacaoArquivoBase));
453
+ for (const sufixo of ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"]) {
454
+ const candidato = path.posix.normalize(path.posix.join(baseDir, `${especificador}${sufixo}`));
455
+ if (/\.(?:ts|tsx|js|jsx)$/i.test(candidato)) {
456
+ return candidato;
457
+ }
458
+ }
459
+ return undefined;
460
+ }
461
+ function extrairImportsTypeScriptConsumer(relacaoArquivo, codigo) {
462
+ const imports = new Map();
463
+ for (const match of codigo.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*["']([^"']+)["']/g)) {
464
+ const arquivoImportado = resolverImportRelativoConsumer(relacaoArquivo, match[2]);
465
+ if (!arquivoImportado) {
466
+ continue;
467
+ }
468
+ for (const bruto of match[1].split(",")) {
469
+ const local = bruto.trim().split(/\s+as\s+/i).at(-1)?.trim();
470
+ if (local) {
471
+ imports.set(local, arquivoImportado);
472
+ }
473
+ }
474
+ }
475
+ for (const match of codigo.matchAll(/import\s+([A-Za-z_]\w*)\s+from\s*["']([^"']+)["']/g)) {
476
+ const arquivoImportado = resolverImportRelativoConsumer(relacaoArquivo, match[2]);
477
+ const local = match[1]?.trim();
478
+ if (arquivoImportado && local) {
479
+ imports.set(local, arquivoImportado);
480
+ }
481
+ }
482
+ return imports;
483
+ }
484
+ function extrairRotasReactViteConsumer(relacaoArquivo, codigo) {
485
+ const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
486
+ const rotas = new Map();
487
+ const registrar = (caminhoCru, componente) => {
488
+ const rota = normalizarRotaDeclaradaConsumer(caminhoCru);
489
+ const chave = `${rota}:${normalizarRelacaoConsumer(relacaoArquivo)}:${componente ?? "router"}`;
490
+ rotas.set(chave, {
491
+ rota,
492
+ arquivoRotas: normalizarRelacaoConsumer(relacaoArquivo),
493
+ arquivoComponente: componente ? imports.get(componente) : undefined,
494
+ });
495
+ };
496
+ for (const match of codigo.matchAll(/(?:path\s*:\s*["'`]([^"'`]*)["'`]|index\s*:\s*true)[\s\S]{0,260}?(?:element\s*:\s*<\s*([A-Za-z_]\w*)|Component\s*:\s*([A-Za-z_]\w*))/g)) {
497
+ const caminhoCru = match[1] ?? "";
498
+ const componente = match[2] ?? match[3];
499
+ registrar(caminhoCru, componente);
500
+ }
501
+ for (const match of codigo.matchAll(/<Route\b[^>]*?(?:path=["'`]([^"'`]*)["'`][^>]*?)?(index\b)?[^>]*?(?:element=\{\s*<\s*([A-Za-z_]\w*)|Component=\{\s*([A-Za-z_]\w*))/g)) {
502
+ const caminhoCru = match[2] ? "" : (match[1] ?? "");
503
+ const componente = match[3] ?? match[4];
504
+ registrar(caminhoCru, componente);
505
+ }
506
+ return [...rotas.values()];
507
+ }
508
+ function normalizarRotaDeclaradaFlutter(caminhoCru) {
509
+ return montarRotaConsumer((caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean));
510
+ }
511
+ function extrairRotasFlutterConsumer(relacaoArquivo, codigo) {
512
+ const rotas = new Map();
513
+ const registrar = (caminhoCru) => {
514
+ const rota = normalizarRotaDeclaradaFlutter(caminhoCru);
515
+ rotas.set(`${rota}:${normalizarRelacaoConsumer(relacaoArquivo)}`, {
516
+ rota,
517
+ arquivoRotas: normalizarRelacaoConsumer(relacaoArquivo),
518
+ });
519
+ };
520
+ for (const match of codigo.matchAll(/GoRoute\s*\([\s\S]{0,220}?path\s*:\s*["'`]([^"'`]+)["'`]/g)) {
521
+ registrar(match[1] ?? "");
522
+ }
523
+ for (const match of codigo.matchAll(/["'`]([^"'`]+)["'`]\s*:\s*\([^)]*\)\s*=>/g)) {
524
+ registrar(match[1] ?? "");
525
+ }
526
+ if (/home\s*:\s*(?:const\s+)?[A-Za-z_]\w*\(/.test(codigo)) {
527
+ registrar("/");
528
+ }
529
+ return [...rotas.values()];
530
+ }
531
+ function extrairRotasAngularConsumerDiretas(relacaoArquivo, codigo, prefixo = "/") {
532
+ const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
533
+ const rotas = [];
534
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?component\s*:\s*([A-Za-z_]\w*)/g)) {
535
+ const caminhoCru = (match[1] ?? "").trim();
536
+ const componente = match[2];
537
+ rotas.push({
538
+ rota: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
539
+ arquivoRotas: normalizarRelacaoConsumer(relacaoArquivo),
540
+ componente,
541
+ arquivoComponente: imports.get(componente),
542
+ });
543
+ }
544
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?loadComponent\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
545
+ const caminhoCru = (match[1] ?? "").trim();
546
+ const arquivoComponente = resolverImportRelativoConsumer(relacaoArquivo, match[2] ?? "");
547
+ rotas.push({
548
+ rota: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
549
+ arquivoRotas: normalizarRelacaoConsumer(relacaoArquivo),
550
+ arquivoComponente,
551
+ });
552
+ }
553
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,360}?loadChildren\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
554
+ const caminhoCru = (match[1] ?? "").trim();
555
+ const arquivoRotasFilhas = resolverImportRelativoConsumer(relacaoArquivo, match[2] ?? "");
556
+ rotas.push({
557
+ rota: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
558
+ arquivoRotas: normalizarRelacaoConsumer(relacaoArquivo),
559
+ arquivoRotasFilhas,
560
+ });
561
+ }
562
+ return rotas;
563
+ }
564
+ async function extrairRotasAngularConsumer(diretorioBase, relacaoArquivo, prefixo = "/", visitados = new Set()) {
565
+ const relacaoNormalizada = normalizarRelacaoConsumer(relacaoArquivo);
566
+ if (visitados.has(relacaoNormalizada)) {
567
+ return [];
568
+ }
569
+ visitados.add(relacaoNormalizada);
570
+ let codigo = "";
571
+ try {
572
+ codigo = await readFile(path.join(diretorioBase, relacaoNormalizada), "utf8");
573
+ }
574
+ catch {
575
+ return [];
576
+ }
577
+ const rotas = extrairRotasAngularConsumerDiretas(relacaoNormalizada, codigo, prefixo);
578
+ const filhas = [];
579
+ for (const rota of rotas) {
580
+ if (!rota.arquivoRotasFilhas) {
581
+ continue;
582
+ }
583
+ filhas.push(...await extrairRotasAngularConsumer(diretorioBase, rota.arquivoRotasFilhas, rota.rota, visitados));
584
+ }
585
+ return [...rotas, ...filhas];
586
+ }
587
+ function simboloEhBridgeConsumer(caminho, arquivo) {
588
+ return arquivoEhBridgeNextJsConsumer(arquivo)
589
+ || arquivoEhBridgeReactViteConsumer(arquivo)
590
+ || arquivoEhBridgeAngularConsumer(arquivo)
591
+ || arquivoEhBridgeFlutterConsumer(arquivo)
592
+ || /(?:^|\.)(?:src\.)?lib\.(?:sema_consumer_bridge|sema\.)/i.test(caminho)
593
+ || /(?:^|\.)(?:src\.)?app\.(?:sema_consumer_bridge|sema\.)/i.test(caminho)
594
+ || /(?:^|\.)(?:lib\.)?(?:sema_consumer_bridge|api\.sema_contract_bridge|sema\.)/i.test(caminho);
595
+ }
596
+ function inferirConsumerFrameworkPrincipal(fontesLegado, consumerSurfaces, consumerBridges) {
597
+ const arquivos = [
598
+ ...consumerSurfaces.map((item) => item.arquivo),
599
+ ...consumerBridges.map((item) => item.arquivo),
600
+ ].map(normalizarRelacaoConsumer);
601
+ if (arquivos.some((arquivo) => /(?:^|\/)(?:src\/)?app\/(?:(?!api\/).)*?(?:page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/i.test(arquivo))) {
602
+ return "nextjs-consumer";
603
+ }
604
+ if (arquivos.some((arquivo) => /^(?:src\/)?pages\/.+\.(?:ts|tsx|js|jsx)$/i.test(arquivo)
605
+ || /^(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(arquivo)
606
+ || /(?:^|\/)(?:src\/)?(?:app\/)?(?:router|routes)\.(?:ts|tsx|js|jsx)$/i.test(arquivo))) {
607
+ return "react-vite-consumer";
608
+ }
609
+ if (arquivos.some((arquivo) => /(?:^|\/)(?:src\/)?app\/.+\.component\.(?:ts|js)$/i.test(arquivo) || arquivoEhRotasAngularConsumer(arquivo))) {
610
+ return "angular-consumer";
611
+ }
612
+ if (arquivos.some((arquivo) => /(?:^|\/)(?:lib\/)?(?:screens|pages)\/.+\.dart$/i.test(arquivo)
613
+ || /(?:^|\/)(?:lib\/)?(?:router|app_router|routes|main)\.dart$/i.test(arquivo))) {
614
+ return "flutter-consumer";
615
+ }
616
+ for (const framework of ["nextjs-consumer", "react-vite-consumer", "angular-consumer", "flutter-consumer"]) {
617
+ if (fontesLegado.includes(framework)) {
618
+ return framework;
619
+ }
620
+ }
621
+ return null;
622
+ }
294
623
  function extrairColecoesFirebase(arquivo, codigo) {
295
624
  const recursos = new Map();
296
625
  const registrar = (nome) => {
@@ -322,11 +651,17 @@ async function indexarTypeScript(diretorios) {
322
651
  const simbolos = new Map();
323
652
  const rotas = [];
324
653
  const recursos = new Map();
654
+ const consumerSurfaces = new Map();
325
655
  for (const diretorio of diretorios) {
326
656
  const arquivos = (await listarArquivosRecursivos(diretorio, [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]))
327
657
  .filter((arquivo) => !arquivo.endsWith(".d.ts")
328
658
  && !arquivo.endsWith(".spec.ts")
329
659
  && !arquivo.endsWith(".test.ts"));
660
+ const arquivosRotasAngular = arquivos.filter((arquivo) => arquivoEhRotasAngularConsumer(path.relative(diretorio, arquivo)));
661
+ const arquivosRotasAngularRaiz = new Set(arquivosRotasAngular
662
+ .filter((arquivo) => arquivoEhRotasAngularConsumerRaiz(path.relative(diretorio, arquivo)))
663
+ .map((arquivo) => path.resolve(arquivo)));
664
+ const usarApenasRotasAngularRaiz = arquivosRotasAngularRaiz.size > 0;
330
665
  for (const arquivo of arquivos) {
331
666
  const codigo = await readFile(arquivo, "utf8");
332
667
  const scriptKind = arquivo.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
@@ -345,6 +680,89 @@ async function indexarTypeScript(diretorios) {
345
680
  simbolo: rota.simbolo,
346
681
  });
347
682
  }
683
+ const superficieNextJs = arquivoEhSuperficieNextJsConsumer(relacao)
684
+ ? inferirRotaNextJsConsumer(relacao)
685
+ : undefined;
686
+ if (superficieNextJs) {
687
+ consumerSurfaces.set(`${superficieNextJs.rota}:${arquivo}:${superficieNextJs.tipoArquivo}`, {
688
+ rota: superficieNextJs.rota,
689
+ arquivo,
690
+ tipoArquivo: superficieNextJs.tipoArquivo,
691
+ });
692
+ rotas.push({
693
+ origem: "nextjs-consumer",
694
+ metodo: "VIEW",
695
+ caminho: superficieNextJs.rota,
696
+ arquivo,
697
+ simbolo: superficieNextJs.tipoArquivo,
698
+ });
699
+ }
700
+ const superficieReact = arquivoEhSuperficieReactViteConsumer(relacao)
701
+ ? inferirRotaReactViteConsumer(relacao)
702
+ : undefined;
703
+ if (superficieReact) {
704
+ consumerSurfaces.set(`${superficieReact.rota}:${arquivo}:${superficieReact.tipoArquivo}`, {
705
+ rota: superficieReact.rota,
706
+ arquivo,
707
+ tipoArquivo: superficieReact.tipoArquivo,
708
+ });
709
+ rotas.push({
710
+ origem: "react-vite-consumer",
711
+ metodo: "VIEW",
712
+ caminho: superficieReact.rota,
713
+ arquivo,
714
+ simbolo: superficieReact.tipoArquivo,
715
+ });
716
+ }
717
+ if (arquivoEhRotasReactViteConsumer(relacao, codigo)) {
718
+ for (const rotaReact of extrairRotasReactViteConsumer(relacao, codigo)) {
719
+ consumerSurfaces.set(`${rotaReact.rota}:${arquivo}:router`, {
720
+ rota: rotaReact.rota,
721
+ arquivo,
722
+ tipoArquivo: "router",
723
+ });
724
+ rotas.push({
725
+ origem: "react-vite-consumer",
726
+ metodo: "VIEW",
727
+ caminho: rotaReact.rota,
728
+ arquivo,
729
+ simbolo: "router",
730
+ });
731
+ if (rotaReact.arquivoComponente) {
732
+ const arquivoComponente = path.join(diretorio, rotaReact.arquivoComponente);
733
+ consumerSurfaces.set(`${rotaReact.rota}:${arquivoComponente}:page`, {
734
+ rota: rotaReact.rota,
735
+ arquivo: arquivoComponente,
736
+ tipoArquivo: "page",
737
+ });
738
+ }
739
+ }
740
+ }
741
+ if (arquivoEhRotasAngularConsumer(relacao) && (!usarApenasRotasAngularRaiz || arquivosRotasAngularRaiz.has(path.resolve(arquivo)))) {
742
+ for (const rotaAngular of await extrairRotasAngularConsumer(diretorio, relacao)) {
743
+ const arquivoRotasAngular = path.join(diretorio, rotaAngular.arquivoRotas);
744
+ consumerSurfaces.set(`${rotaAngular.rota}:${arquivoRotasAngular}:routes`, {
745
+ rota: rotaAngular.rota,
746
+ arquivo: arquivoRotasAngular,
747
+ tipoArquivo: "routes",
748
+ });
749
+ rotas.push({
750
+ origem: "angular-consumer",
751
+ metodo: "VIEW",
752
+ caminho: rotaAngular.rota,
753
+ arquivo: arquivoRotasAngular,
754
+ simbolo: rotaAngular.componente ?? "routes",
755
+ });
756
+ if (rotaAngular.arquivoComponente) {
757
+ const arquivoComponente = path.join(diretorio, rotaAngular.arquivoComponente);
758
+ consumerSurfaces.set(`${rotaAngular.rota}:${arquivoComponente}:component`, {
759
+ rota: rotaAngular.rota,
760
+ arquivo: arquivoComponente,
761
+ tipoArquivo: "component",
762
+ });
763
+ }
764
+ }
765
+ }
348
766
  for (const node of sourceFile.statements) {
349
767
  if (ts.isFunctionDeclaration(node) && node.name) {
350
768
  registrarSimboloTypeScript(simbolos, basesSimbolicas, arquivo, node.name.text);
@@ -396,7 +814,14 @@ async function indexarTypeScript(diretorios) {
396
814
  }
397
815
  }
398
816
  }
399
- return { simbolos: [...simbolos.values()], rotas, recursos: [...recursos.values()] };
817
+ return {
818
+ simbolos: [...simbolos.values()],
819
+ rotas,
820
+ recursos: [...recursos.values()],
821
+ consumerSurfaces: [...consumerSurfaces.values()].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
822
+ || a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
823
+ || a.arquivo.localeCompare(b.arquivo, "pt-BR")),
824
+ };
400
825
  }
401
826
  function registrarSimboloPython(simbolos, basesSimbolicas, arquivo, nome, nomeClasse) {
402
827
  for (const baseSimbolica of basesSimbolicas) {
@@ -493,12 +918,15 @@ async function indexarPython(diretorios) {
493
918
  }
494
919
  async function indexarDart(diretorios) {
495
920
  const simbolos = new Map();
921
+ const rotas = [];
922
+ const consumerSurfaces = new Map();
496
923
  for (const diretorio of diretorios) {
497
924
  const arquivos = (await listarArquivosRecursivos(diretorio, [".dart"]))
498
925
  .filter((arquivo) => !arquivo.endsWith(".g.dart") && !arquivo.endsWith(".freezed.dart"));
499
926
  for (const arquivo of arquivos) {
500
927
  const texto = await readFile(arquivo, "utf8");
501
928
  const basesSimbolicas = caminhosSimbolicos(diretorio, arquivo);
929
+ const relacao = path.relative(diretorio, arquivo);
502
930
  for (const match of texto.matchAll(/(?:Future<[^\n]+>|[\w?<>.,\s]+)\s+(\w+)\(([^)]*)\)\s*(?:async\s*)?\{/g)) {
503
931
  const nome = match[1];
504
932
  if (["build", "toString"].includes(nome)) {
@@ -509,9 +937,46 @@ async function indexarDart(diretorios) {
509
937
  simbolos.set(caminho, { origem: "dart", caminho, arquivo, simbolo: nome });
510
938
  }
511
939
  }
940
+ const superficieFlutter = inferirRotaFlutterConsumer(relacao);
941
+ if (superficieFlutter) {
942
+ consumerSurfaces.set(`${superficieFlutter.rota}:${arquivo}:${superficieFlutter.tipoArquivo}`, {
943
+ rota: superficieFlutter.rota,
944
+ arquivo,
945
+ tipoArquivo: superficieFlutter.tipoArquivo,
946
+ });
947
+ rotas.push({
948
+ origem: "flutter-consumer",
949
+ metodo: "VIEW",
950
+ caminho: superficieFlutter.rota,
951
+ arquivo,
952
+ simbolo: superficieFlutter.tipoArquivo,
953
+ });
954
+ }
955
+ if (arquivoEhRotasFlutterConsumer(relacao, texto)) {
956
+ for (const rotaFlutter of extrairRotasFlutterConsumer(relacao, texto)) {
957
+ consumerSurfaces.set(`${rotaFlutter.rota}:${arquivo}:router`, {
958
+ rota: rotaFlutter.rota,
959
+ arquivo,
960
+ tipoArquivo: "router",
961
+ });
962
+ rotas.push({
963
+ origem: "flutter-consumer",
964
+ metodo: "VIEW",
965
+ caminho: rotaFlutter.rota,
966
+ arquivo,
967
+ simbolo: "router",
968
+ });
969
+ }
970
+ }
512
971
  }
513
972
  }
514
- return [...simbolos.values()];
973
+ return {
974
+ simbolos: [...simbolos.values()],
975
+ rotas,
976
+ consumerSurfaces: [...consumerSurfaces.values()].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
977
+ || a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
978
+ || a.arquivo.localeCompare(b.arquivo, "pt-BR")),
979
+ };
515
980
  }
516
981
  function registrarSimboloGenerico(simbolos, origem, basesSimbolicas, arquivo, simbolo) {
517
982
  for (const baseSimbolica of basesSimbolicas) {
@@ -871,7 +1336,7 @@ export async function analisarDriftLegado(contexto) {
871
1336
  const todosSimbolos = [
872
1337
  ...indexTs.simbolos,
873
1338
  ...indexPy.simbolos,
874
- ...indexDart,
1339
+ ...indexDart.simbolos,
875
1340
  ...indexDotnet.simbolos,
876
1341
  ...indexJava.simbolos,
877
1342
  ...indexGo.simbolos,
@@ -881,7 +1346,7 @@ export async function analisarDriftLegado(contexto) {
881
1346
  const mapaImpl = new Map([
882
1347
  ...indexTs.simbolos.map((item) => [item.caminho, item]),
883
1348
  ...indexPy.simbolos.map((item) => [item.caminho, item]),
884
- ...indexDart.map((item) => [item.caminho, item]),
1349
+ ...indexDart.simbolos.map((item) => [item.caminho, item]),
885
1350
  ...indexDotnet.simbolos.map((item) => [item.caminho, item]),
886
1351
  ...indexJava.simbolos.map((item) => [item.caminho, item]),
887
1352
  ...indexGo.simbolos.map((item) => [item.caminho, item]),
@@ -892,6 +1357,7 @@ export async function analisarDriftLegado(contexto) {
892
1357
  const todasRotasIndexadas = [
893
1358
  ...indexTs.rotas,
894
1359
  ...indexPy.rotas,
1360
+ ...indexDart.rotas,
895
1361
  ...indexDotnet.rotas,
896
1362
  ...indexJava.rotas,
897
1363
  ...indexGo.rotas,
@@ -1051,7 +1517,11 @@ export async function analisarDriftLegado(contexto) {
1051
1517
  if (!esperadas.length || !route.metodo || !route.caminho) {
1052
1518
  continue;
1053
1519
  }
1054
- const encontradas = todasRotasIndexadas.filter((rotaResolvida) => esperadas.includes(rotaResolvida.origem));
1520
+ const encontradas = todasRotasIndexadas.filter((rotaResolvida) => rotaResolvida.origem !== "nextjs-consumer"
1521
+ && rotaResolvida.origem !== "react-vite-consumer"
1522
+ && rotaResolvida.origem !== "angular-consumer"
1523
+ && rotaResolvida.origem !== "flutter-consumer"
1524
+ && esperadas.includes(rotaResolvida.origem));
1055
1525
  const combina = encontradas.some((rotaResolvida) => rotaResolvida.metodo === route.metodo
1056
1526
  && normalizarCaminhoRota(rotaResolvida.caminho) === normalizarCaminhoRota(route.caminho));
1057
1527
  if (!combina) {
@@ -1197,9 +1667,30 @@ export async function analisarDriftLegado(contexto) {
1197
1667
  resumo.lacunas.includes("vinculo_quebrado") ? "corrigir vinculos rastreaveis" : "",
1198
1668
  ].filter(Boolean))];
1199
1669
  }
1670
+ const consumerSurfaces = [...indexTs.consumerSurfaces, ...indexDart.consumerSurfaces].sort((a, b) => a.rota.localeCompare(b.rota, "pt-BR")
1671
+ || a.tipoArquivo.localeCompare(b.tipoArquivo, "pt-BR")
1672
+ || a.arquivo.localeCompare(b.arquivo, "pt-BR"));
1673
+ const consumerBridges = [...new Map([...indexTs.simbolos, ...indexDart.simbolos]
1674
+ .filter((simbolo) => simboloEhBridgeConsumer(simbolo.caminho, simbolo.arquivo))
1675
+ .map((simbolo) => [
1676
+ `${simbolo.caminho}:${simbolo.arquivo}:${simbolo.simbolo}`,
1677
+ {
1678
+ caminho: simbolo.caminho,
1679
+ arquivo: simbolo.arquivo,
1680
+ simbolo: simbolo.simbolo,
1681
+ },
1682
+ ])).values()].sort((a, b) => a.caminho.localeCompare(b.caminho, "pt-BR")
1683
+ || a.arquivo.localeCompare(b.arquivo, "pt-BR"));
1684
+ const appRoutes = [...new Set(consumerSurfaces.map((surface) => surface.rota))]
1685
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
1686
+ const consumerFramework = inferirConsumerFrameworkPrincipal(contexto.fontesLegado, consumerSurfaces, consumerBridges);
1200
1687
  const payloadBase = {
1201
1688
  comando: "drift",
1202
1689
  sucesso: implsQuebrados.length === 0 && rotasDivergentes.length === 0 && recursosDivergentes.length === 0 && vinculosQuebrados.length === 0,
1690
+ consumerFramework,
1691
+ appRoutes,
1692
+ consumerSurfaces,
1693
+ consumerBridges,
1203
1694
  modulos: contexto.modulosSelecionados.map((item) => ({
1204
1695
  caminho: item.caminho,
1205
1696
  modulo: item.resultado.ir?.nome ?? item.resultado.modulo?.nome ?? null,