@semacode/cli 1.2.17 → 1.3.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 (128) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +58 -10
  3. package/dist/projeto.js +4 -1
  4. package/package.json +17 -34
  5. package/src/cpp-symbols.ts +82 -0
  6. package/src/dotnet-http.ts +355 -0
  7. package/src/drift.ts +2455 -0
  8. package/src/go-http.ts +118 -0
  9. package/src/importador.ts +3448 -0
  10. package/src/index.ts +4470 -0
  11. package/src/java-http.ts +247 -0
  12. package/src/projeto.ts +810 -0
  13. package/src/python-http.ts +258 -0
  14. package/src/rust-http.ts +125 -0
  15. package/src/tipos.ts +22 -0
  16. package/src/typescript-http.ts +1076 -0
  17. package/tsconfig.json +20 -0
  18. package/AGENTS.md +0 -50
  19. package/LICENSE +0 -22
  20. package/SEMA_BRIEF.curto.txt +0 -9
  21. package/SEMA_BRIEF.md +0 -49
  22. package/SEMA_BRIEF.micro.txt +0 -7
  23. package/SEMA_INDEX.json +0 -546
  24. package/dist/cpp-symbols.d.ts +0 -10
  25. package/dist/cpp-symbols.js.map +0 -1
  26. package/dist/dotnet-http.d.ts +0 -23
  27. package/dist/dotnet-http.js.map +0 -1
  28. package/dist/drift.d.ts +0 -118
  29. package/dist/drift.js.map +0 -1
  30. package/dist/go-http.d.ts +0 -23
  31. package/dist/go-http.js.map +0 -1
  32. package/dist/importador.d.ts +0 -29
  33. package/dist/importador.js.map +0 -1
  34. package/dist/index.d.ts +0 -2
  35. package/dist/index.js.map +0 -1
  36. package/dist/java-http.d.ts +0 -23
  37. package/dist/java-http.js.map +0 -1
  38. package/dist/lua-symbols.d.ts +0 -8
  39. package/dist/lua-symbols.js.map +0 -1
  40. package/dist/projeto.d.ts +0 -48
  41. package/dist/projeto.js.map +0 -1
  42. package/dist/python-http.d.ts +0 -23
  43. package/dist/python-http.js.map +0 -1
  44. package/dist/rust-http.d.ts +0 -23
  45. package/dist/rust-http.js.map +0 -1
  46. package/dist/tipos.d.ts +0 -3
  47. package/dist/tipos.js.map +0 -1
  48. package/dist/typescript-http.d.ts +0 -35
  49. package/dist/typescript-http.js.map +0 -1
  50. package/docs/AGENT_STARTER.md +0 -102
  51. package/docs/como-ensinar-a-sema-para-ia.md +0 -149
  52. package/docs/fluxo-pratico-ia-sema.md +0 -177
  53. package/docs/instalacao-e-primeiro-uso.md +0 -196
  54. package/docs/integracao-com-ia.md +0 -228
  55. package/docs/pagamento-ponta-a-ponta.md +0 -155
  56. package/docs/prompt-base-ia-sema.md +0 -104
  57. package/docs/sintaxe.md +0 -361
  58. package/exemplos/automacao.sema +0 -107
  59. package/exemplos/cadastro_usuario.sema +0 -54
  60. package/exemplos/calculadora.sema +0 -78
  61. package/exemplos/crud_simples.sema +0 -89
  62. package/exemplos/operacao_estrategia.sema +0 -402
  63. package/exemplos/pagamento.sema +0 -222
  64. package/exemplos/pagamento_dominio.sema +0 -35
  65. package/exemplos/testes_embutidos.sema +0 -45
  66. package/exemplos/tratamento_erro.sema +0 -157
  67. package/llms-full.txt +0 -34
  68. package/llms.txt +0 -17
  69. package/node_modules/@sema/gerador-dart/dist/index.d.ts +0 -3
  70. package/node_modules/@sema/gerador-dart/dist/index.js +0 -44
  71. package/node_modules/@sema/gerador-dart/dist/index.js.map +0 -1
  72. package/node_modules/@sema/gerador-dart/package.json +0 -7
  73. package/node_modules/@sema/gerador-lua/dist/index.d.ts +0 -3
  74. package/node_modules/@sema/gerador-lua/dist/index.js +0 -328
  75. package/node_modules/@sema/gerador-lua/dist/index.js.map +0 -1
  76. package/node_modules/@sema/gerador-lua/package.json +0 -7
  77. package/node_modules/@sema/gerador-python/dist/index.d.ts +0 -6
  78. package/node_modules/@sema/gerador-python/dist/index.js +0 -628
  79. package/node_modules/@sema/gerador-python/dist/index.js.map +0 -1
  80. package/node_modules/@sema/gerador-python/package.json +0 -7
  81. package/node_modules/@sema/gerador-typescript/dist/index.d.ts +0 -6
  82. package/node_modules/@sema/gerador-typescript/dist/index.js +0 -656
  83. package/node_modules/@sema/gerador-typescript/dist/index.js.map +0 -1
  84. package/node_modules/@sema/gerador-typescript/package.json +0 -7
  85. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +0 -122
  86. package/node_modules/@sema/nucleo/dist/ast/tipos.js +0 -2
  87. package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +0 -1
  88. package/node_modules/@sema/nucleo/dist/diagnosticos/index.d.ts +0 -21
  89. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js +0 -12
  90. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +0 -1
  91. package/node_modules/@sema/nucleo/dist/formatador/index.d.ts +0 -9
  92. package/node_modules/@sema/nucleo/dist/formatador/index.js +0 -445
  93. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +0 -1
  94. package/node_modules/@sema/nucleo/dist/index.d.ts +0 -34
  95. package/node_modules/@sema/nucleo/dist/index.js +0 -95
  96. package/node_modules/@sema/nucleo/dist/index.js.map +0 -1
  97. package/node_modules/@sema/nucleo/dist/ir/conversor.d.ts +0 -5
  98. package/node_modules/@sema/nucleo/dist/ir/conversor.js +0 -781
  99. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
  100. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +0 -285
  101. package/node_modules/@sema/nucleo/dist/ir/modelos.js +0 -2
  102. package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +0 -1
  103. package/node_modules/@sema/nucleo/dist/lexer/lexer.d.ts +0 -7
  104. package/node_modules/@sema/nucleo/dist/lexer/lexer.js +0 -122
  105. package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +0 -1
  106. package/node_modules/@sema/nucleo/dist/lexer/tokens.d.ts +0 -8
  107. package/node_modules/@sema/nucleo/dist/lexer/tokens.js +0 -46
  108. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +0 -1
  109. package/node_modules/@sema/nucleo/dist/parser/parser.d.ts +0 -9
  110. package/node_modules/@sema/nucleo/dist/parser/parser.js +0 -656
  111. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +0 -1
  112. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +0 -57
  113. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +0 -1497
  114. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +0 -1
  115. package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +0 -104
  116. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +0 -445
  117. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +0 -1
  118. package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +0 -91
  119. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +0 -258
  120. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +0 -1
  121. package/node_modules/@sema/nucleo/dist/util/arquivos.d.ts +0 -2
  122. package/node_modules/@sema/nucleo/dist/util/arquivos.js +0 -25
  123. package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +0 -1
  124. package/node_modules/@sema/nucleo/package.json +0 -7
  125. package/node_modules/@sema/padroes/dist/index.d.ts +0 -21
  126. package/node_modules/@sema/padroes/dist/index.js +0 -159
  127. package/node_modules/@sema/padroes/dist/index.js.map +0 -1
  128. package/node_modules/@sema/padroes/package.json +0 -7
package/README.md CHANGED
@@ -36,7 +36,7 @@ sema --help
36
36
  ## Instalacao via tarball da release
37
37
 
38
38
  ```bash
39
- npm install -g ./sema-cli-1.2.17.tgz
39
+ npm install -g ./{{TGZ_ARQUIVO}}
40
40
  ```
41
41
 
42
42
  Ou direto da GitHub Release:
@@ -55,7 +55,7 @@ npx sema --help
55
55
  Ou, se voce estiver testando um tarball local:
56
56
 
57
57
  ```bash
58
- npm install ./sema-cli-1.2.17.tgz
58
+ npm install ./{{TGZ_ARQUIVO}}
59
59
  npx sema --help
60
60
  ```
61
61
 
package/dist/index.js CHANGED
@@ -10,6 +10,9 @@ import { gerarDart } from "@sema/gerador-dart";
10
10
  import { gerarLua } from "@sema/gerador-lua";
11
11
  import { gerarPython } from "@sema/gerador-python";
12
12
  import { gerarTypeScript } from "@sema/gerador-typescript";
13
+ import { gerarJavaScript } from "@sema/gerador-javascript";
14
+ import { gerarHtml } from "@sema/gerador-html";
15
+ import { gerarCss } from "@sema/gerador-css";
13
16
  import { carregarConfiguracaoProjeto, carregarProjeto, resolverAlvoPadrao, resolverAlvosVerificacao, resolverEstruturaSaidaPadrao, resolverFrameworkPadrao, resolverSaidaPadrao, } from "./projeto.js";
14
17
  import { importarProjetoLegado, resumoImportacao } from "./importador.js";
15
18
  import { analisarDriftLegado } from "./drift.js";
@@ -518,7 +521,7 @@ function validarCompatibilidadeFramework(alvo, framework) {
518
521
  if (framework === "fastapi" && alvo !== "python") {
519
522
  return `Framework "${framework}" so pode ser usado com o alvo python.`;
520
523
  }
521
- if (alvo === "dart" || alvo === "lua") {
524
+ if (alvo === "dart" || alvo === "lua" || alvo === "javascript" || alvo === "html" || alvo === "css") {
522
525
  return `Framework "${framework}" nao e suportado para o alvo ${alvo}.`;
523
526
  }
524
527
  return undefined;
@@ -627,6 +630,15 @@ function gerarArquivosPorAlvo(ir, alvo, framework) {
627
630
  if (alvo === "lua") {
628
631
  return gerarLua(ir);
629
632
  }
633
+ if (alvo === "javascript") {
634
+ return gerarJavaScript(ir);
635
+ }
636
+ if (alvo === "html") {
637
+ return gerarHtml(ir);
638
+ }
639
+ if (alvo === "css") {
640
+ return gerarCss(ir);
641
+ }
630
642
  return gerarTypeScript(ir, { framework });
631
643
  }
632
644
  function aplicarEstruturaSaida(arquivos, ir, estrutura) {
@@ -665,6 +677,19 @@ function aplicarEstruturaSaida(arquivos, ir, estrutura) {
665
677
  novoBasename = `test_${nomeArquivo}.lua`;
666
678
  conteudo = conteudo.replace(`${nomeBaseAntigo}.lua`, `${nomeArquivo}.lua`);
667
679
  }
680
+ else if (basename === `${nomeBaseAntigo}.js`) {
681
+ novoBasename = `${nomeArquivo}.js`;
682
+ }
683
+ else if (basename === `${nomeBaseAntigo}.test.js`) {
684
+ novoBasename = `${nomeArquivo}.test.js`;
685
+ conteudo = conteudo.replace(`./${nomeBaseAntigo}.js`, `./${nomeArquivo}.js`);
686
+ }
687
+ else if (basename === `${nomeBaseAntigo}.html`) {
688
+ novoBasename = `${nomeArquivo}.html`;
689
+ }
690
+ else if (basename === `${nomeBaseAntigo}.css`) {
691
+ novoBasename = `${nomeArquivo}.css`;
692
+ }
668
693
  return {
669
694
  caminhoRelativo: pastaModulo ? path.join(pastaModulo, novoBasename) : novoBasename,
670
695
  conteudo,
@@ -672,7 +697,7 @@ function aplicarEstruturaSaida(arquivos, ir, estrutura) {
672
697
  });
673
698
  }
674
699
  function contarCasosDeTesteGerados(alvo, arquivos) {
675
- if (alvo === "dart") {
700
+ if (alvo === "dart" || alvo === "html" || alvo === "css") {
676
701
  return 0;
677
702
  }
678
703
  if (alvo === "lua") {
@@ -689,6 +714,13 @@ function contarCasosDeTesteGerados(alvo, arquivos) {
689
714
  }
690
715
  return (arquivoTeste.conteudo.match(/\btest\(/g) ?? []).length;
691
716
  }
717
+ if (alvo === "javascript") {
718
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.js"));
719
+ if (!arquivoTeste) {
720
+ return 0;
721
+ }
722
+ return (arquivoTeste.conteudo.match(/\btest\(/g) ?? []).length;
723
+ }
692
724
  const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_"));
693
725
  if (!arquivoTeste) {
694
726
  return 0;
@@ -699,17 +731,14 @@ function executarTestesGerados(alvo, baseSaida, arquivos, silencioso = false) {
699
731
  const quantidadeTestes = contarCasosDeTesteGerados(alvo, arquivos);
700
732
  if (quantidadeTestes === 0) {
701
733
  if (!silencioso) {
702
- const nomeAlvo = alvo === "typescript"
703
- ? "TypeScript"
704
- : alvo === "python"
705
- ? "Python"
706
- : alvo === "lua"
707
- ? "Lua"
708
- : "Dart";
709
- console.log(`Nenhum teste ${nomeAlvo} foi gerado.`);
734
+ const nomesAlvo = { typescript: "TypeScript", python: "Python", lua: "Lua", dart: "Dart", javascript: "JavaScript", html: "HTML", css: "CSS" };
735
+ console.log(`Nenhum teste ${nomesAlvo[alvo] ?? alvo} foi gerado.`);
710
736
  }
711
737
  return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
712
738
  }
739
+ if (alvo === "html" || alvo === "css") {
740
+ return { codigoSaida: 0, quantidadeTestes: 0, saidaPadrao: "", saidaErro: "" };
741
+ }
713
742
  if (alvo === "typescript") {
714
743
  const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.ts"))?.caminhoRelativo;
715
744
  if (!arquivoTeste) {
@@ -729,6 +758,25 @@ function executarTestesGerados(alvo, baseSaida, arquivos, silencioso = false) {
729
758
  saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
730
759
  };
731
760
  }
761
+ if (alvo === "javascript") {
762
+ const arquivoTeste = arquivos.find((item) => item.caminhoRelativo.endsWith(".test.js"))?.caminhoRelativo;
763
+ if (!arquivoTeste) {
764
+ if (!silencioso) {
765
+ console.log("Nenhum teste JavaScript foi gerado.");
766
+ }
767
+ return { codigoSaida: 0, quantidadeTestes, saidaPadrao: "", saidaErro: "" };
768
+ }
769
+ const execucao = spawnSync("node", ["--test", path.join(baseSaida, arquivoTeste)], {
770
+ stdio: silencioso ? "pipe" : "inherit",
771
+ encoding: silencioso ? "utf8" : undefined,
772
+ });
773
+ return {
774
+ codigoSaida: execucao.status ?? 1,
775
+ quantidadeTestes,
776
+ saidaPadrao: typeof execucao.stdout === "string" ? execucao.stdout : "",
777
+ saidaErro: typeof execucao.stderr === "string" ? execucao.stderr : "",
778
+ };
779
+ }
732
780
  if (alvo === "lua") {
733
781
  const arquivoTeste = arquivos.find((item) => path.basename(item.caminhoRelativo).startsWith("test_") && item.caminhoRelativo.endsWith(".lua"))?.caminhoRelativo;
734
782
  if (!arquivoTeste) {
package/dist/projeto.js CHANGED
@@ -59,9 +59,12 @@ export function normalizarFrameworkGeracao(valor) {
59
59
  return "base";
60
60
  }
61
61
  function normalizarAlvo(valor) {
62
- if (valor === "typescript" || valor === "python" || valor === "dart" || valor === "lua") {
62
+ if (valor === "typescript" || valor === "python" || valor === "dart" || valor === "lua" || valor === "javascript" || valor === "html" || valor === "css") {
63
63
  return valor;
64
64
  }
65
+ if (valor === "js") {
66
+ return "javascript";
67
+ }
65
68
  return undefined;
66
69
  }
67
70
  function resolverEntradaPadrao(cwd, configCarregada) {
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@semacode/cli",
3
- "version": "1.2.17",
4
- "description": "CLI da Sema para validar contratos, medir drift e governar a navegacao operacional de agentes sobre software vivo em backend e front consumer.",
3
+ "version": "1.3.0",
5
4
  "type": "module",
6
- "icon": "logo.png",
5
+ "description": "CLI da Sema para validar contratos, medir drift e governar a navegacao operacional de agentes sobre software vivo em backend e front consumer.",
7
6
  "license": "MIT",
8
7
  "repository": {
9
8
  "type": "git",
@@ -28,41 +27,25 @@
28
27
  "publishConfig": {
29
28
  "access": "public"
30
29
  },
30
+ "icon": "logo.png",
31
31
  "bin": {
32
32
  "sema": "dist/index.js"
33
33
  },
34
34
  "main": "dist/index.js",
35
35
  "types": "dist/index.d.ts",
36
- "files": [
37
- "dist",
38
- "docs",
39
- "exemplos",
40
- "AGENTS.md",
41
- "llms.txt",
42
- "llms-full.txt",
43
- "SEMA_BRIEF.md",
44
- "SEMA_BRIEF.micro.txt",
45
- "SEMA_BRIEF.curto.txt",
46
- "SEMA_INDEX.json",
47
- "logo.png",
48
- "README.md",
49
- "LICENSE"
50
- ],
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json"
38
+ },
51
39
  "dependencies": {
52
- "@sema/nucleo": "1.2.17",
53
- "@sema/gerador-dart": "1.2.17",
54
- "@sema/gerador-lua": "1.2.17",
55
- "@sema/gerador-python": "1.2.17",
56
- "@sema/gerador-typescript": "1.2.17",
57
- "@sema/padroes": "1.2.17",
40
+ "@sema/nucleo": "file:../nucleo",
41
+ "@sema/gerador-dart": "file:../gerador-dart",
42
+ "@sema/gerador-lua": "file:../gerador-lua",
43
+ "@sema/gerador-python": "file:../gerador-python",
44
+ "@sema/gerador-typescript": "file:../gerador-typescript",
45
+ "@sema/gerador-javascript": "file:../gerador-javascript",
46
+ "@sema/gerador-html": "file:../gerador-html",
47
+ "@sema/gerador-css": "file:../gerador-css",
48
+ "@sema/padroes": "file:../padroes",
58
49
  "typescript": "^5.8.3"
59
- },
60
- "bundledDependencies": [
61
- "@sema/nucleo",
62
- "@sema/gerador-dart",
63
- "@sema/gerador-lua",
64
- "@sema/gerador-python",
65
- "@sema/gerador-typescript",
66
- "@sema/padroes"
67
- ]
68
- }
50
+ }
51
+ }
@@ -0,0 +1,82 @@
1
+ export interface SimboloCppExtraido {
2
+ simbolo: string;
3
+ retorno?: string;
4
+ parametros: Array<{ nome: string; tipoTexto?: string; obrigatorio: boolean }>;
5
+ }
6
+
7
+ function extrairParametrosCpp(assinatura: string): Array<{ nome: string; tipoTexto?: string; obrigatorio: boolean }> {
8
+ return assinatura.split(",").map((parametroBruto) => {
9
+ const parametro = parametroBruto.trim();
10
+ if (!parametro || parametro === "void") {
11
+ return undefined;
12
+ }
13
+ const semPadrao = parametro.split("=")[0]?.trim() ?? parametro;
14
+ const partes = semPadrao.split(/\s+/).filter(Boolean);
15
+ if (partes.length < 2) {
16
+ return undefined;
17
+ }
18
+ const nome = partes.at(-1)!.replace(/[&*]+$/, "");
19
+ const tipoTexto = partes.slice(0, -1).join(" ");
20
+ return {
21
+ nome,
22
+ tipoTexto,
23
+ obrigatorio: !parametro.includes("="),
24
+ };
25
+ }).filter((item): item is NonNullable<typeof item> => Boolean(item));
26
+ }
27
+
28
+ export function extrairSimbolosCpp(codigo: string): SimboloCppExtraido[] {
29
+ const simbolos = new Map<string, SimboloCppExtraido>();
30
+
31
+ for (const match of codigo.matchAll(/(?:^|\n)\s*(?:inline\s+|static\s+|virtual\s+|constexpr\s+|friend\s+|extern\s+|template\s*<[^>]+>\s*)*(?:[\w:<>~*&]+\s+)+([A-Za-z_]\w*)::([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:const)?\s*(?:\{|;)/g)) {
32
+ const simbolo = `${match[1]!}.${match[2]!}`;
33
+ simbolos.set(simbolo, {
34
+ simbolo,
35
+ parametros: extrairParametrosCpp(match[3] ?? ""),
36
+ });
37
+ }
38
+
39
+ for (const match of codigo.matchAll(/(?:^|\n)\s*(?:inline\s+|static\s+|virtual\s+|constexpr\s+|friend\s+|extern\s+)*(?:[\w:<>~*&]+\s+)+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:const)?\s*(?:\{|;)/g)) {
40
+ const nome = match[1]!;
41
+ if (["if", "for", "while", "switch", "return"].includes(nome)) {
42
+ continue;
43
+ }
44
+ if (!simbolos.has(nome)) {
45
+ simbolos.set(nome, {
46
+ simbolo: nome,
47
+ parametros: extrairParametrosCpp(match[2] ?? ""),
48
+ });
49
+ }
50
+ }
51
+
52
+ const pilhaClasses: string[] = [];
53
+ for (const linha of codigo.split(/\r?\n/)) {
54
+ const trim = linha.trim();
55
+ const classe = trim.match(/^(?:class|struct)\s+([A-Za-z_]\w*)/);
56
+ if (classe) {
57
+ pilhaClasses.push(classe[1]!);
58
+ continue;
59
+ }
60
+ if (trim.startsWith("};") || trim === "}" || trim === "};") {
61
+ pilhaClasses.pop();
62
+ continue;
63
+ }
64
+
65
+ const metodoClasse = trim.match(/^(?:inline\s+|static\s+|virtual\s+|constexpr\s+)*(?:[\w:<>~*&]+\s+)+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:const)?\s*\{/);
66
+ if (metodoClasse && pilhaClasses.length > 0) {
67
+ const nomeClasse = pilhaClasses[pilhaClasses.length - 1]!;
68
+ const nomeMetodo = metodoClasse[1]!;
69
+ if (!["if", "for", "while", "switch"].includes(nomeMetodo)) {
70
+ const simbolo = `${nomeClasse}.${nomeMetodo}`;
71
+ if (!simbolos.has(simbolo)) {
72
+ simbolos.set(simbolo, {
73
+ simbolo,
74
+ parametros: extrairParametrosCpp(metodoClasse[2] ?? ""),
75
+ });
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ return [...simbolos.values()];
82
+ }
@@ -0,0 +1,355 @@
1
+ export interface ParametroRotaBackend {
2
+ nome: string;
3
+ tipoSema: "Texto" | "Inteiro" | "Decimal" | "Id";
4
+ }
5
+
6
+ export interface SimboloDotnetExtraido {
7
+ simbolo: string;
8
+ retorno?: string;
9
+ parametros: Array<{ nome: string; tipoTexto?: string; obrigatorio: boolean }>;
10
+ }
11
+
12
+ export interface RotaDotnetExtraida {
13
+ origem: "dotnet";
14
+ metodo: string;
15
+ caminho: string;
16
+ simbolo: string;
17
+ parametros: ParametroRotaBackend[];
18
+ retorno?: string;
19
+ }
20
+
21
+ const METODOS_HTTP = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
22
+
23
+ function normalizarCaminhoBase(caminho?: string): string | undefined {
24
+ if (!caminho) {
25
+ return undefined;
26
+ }
27
+ return caminho.replace(/^\/+|\/+$/g, "");
28
+ }
29
+
30
+ function juntarCaminho(base: string | undefined, sufixo: string | undefined): string {
31
+ const partes = [base, sufixo]
32
+ .map((parte) => normalizarCaminhoBase(parte))
33
+ .filter((parte): parte is string => Boolean(parte));
34
+ return `/${partes.join("/")}`.replace(/\/+/g, "/");
35
+ }
36
+
37
+ function normalizarCaminhoAspNet(caminho: string, classe?: string, metodo?: string): string {
38
+ const controller = (classe ?? "").replace(/Controller$/i, "");
39
+ const action = metodo ?? "";
40
+ return caminho
41
+ .replace(/\[controller\]/gi, controller ? controller.toLowerCase() : "controller")
42
+ .replace(/\[action\]/gi, action ? action.toLowerCase() : "action")
43
+ .replace(/\{([^}:]+):[^}]+\}/g, "{$1}")
44
+ .replace(/\/+/g, "/");
45
+ }
46
+
47
+ function mapearTipoRotaDotnet(tipo?: string): ParametroRotaBackend["tipoSema"] {
48
+ const normalizado = (tipo ?? "").toLowerCase();
49
+ if (/(^|\.)(int|int32|int64|long|short)$/.test(normalizado)) {
50
+ return "Inteiro";
51
+ }
52
+ if (/(^|\.)(float|double|decimal)$/.test(normalizado)) {
53
+ return "Decimal";
54
+ }
55
+ if (/guid|uuid|id$/i.test(normalizado)) {
56
+ return "Id";
57
+ }
58
+ return "Texto";
59
+ }
60
+
61
+ function extrairParametrosRota(caminho: string, assinatura: string): ParametroRotaBackend[] {
62
+ const tiposAssinatura = new Map<string, string>();
63
+ for (const parametroBruto of assinatura.split(",")) {
64
+ const parametro = parametroBruto.trim();
65
+ if (!parametro) {
66
+ continue;
67
+ }
68
+ const semPadrao = parametro.split("=")[0]?.trim() ?? parametro;
69
+ const partes = semPadrao.split(/\s+/).filter(Boolean);
70
+ if (partes.length < 2) {
71
+ continue;
72
+ }
73
+ const nome = partes.at(-1)!;
74
+ const tipo = partes.slice(0, -1).join(" ");
75
+ tiposAssinatura.set(nome, tipo);
76
+ }
77
+
78
+ return [...caminho.matchAll(/\{([^}:]+)(?::[^}]+)?\}/g)].map((match) => {
79
+ const nome = match[1]!;
80
+ return {
81
+ nome,
82
+ tipoSema: mapearTipoRotaDotnet(tiposAssinatura.get(nome)),
83
+ };
84
+ });
85
+ }
86
+
87
+ function extrairTextoAtributo(atributo: string): string | undefined {
88
+ return atributo.match(/"([^"]+)"/)?.[1];
89
+ }
90
+
91
+ function extrairMetodosAtributo(atributo: string): string[] {
92
+ const direto = atributo.match(/\[\s*Http(Get|Post|Put|Patch|Delete)\b/i)?.[1]?.toUpperCase();
93
+ if (direto && METODOS_HTTP.has(direto)) {
94
+ return [direto];
95
+ }
96
+
97
+ const bloco = atributo.match(/\bHttpMethods\.(Get|Post|Put|Patch|Delete)\b/gi)
98
+ ?.map((item) => item.split(".").pop()?.toUpperCase() ?? "")
99
+ .filter((item) => METODOS_HTTP.has(item));
100
+ if (bloco && bloco.length > 0) {
101
+ return [...new Set(bloco)];
102
+ }
103
+
104
+ const requestMapping = atributo.match(/\[\s*AcceptVerbs\(([^)]*)\)\s*\]/i)?.[1];
105
+ if (requestMapping) {
106
+ const encontrados = [...requestMapping.matchAll(/"([A-Za-z]+)"/g)]
107
+ .map((match) => match[1]!.toUpperCase())
108
+ .filter((item) => METODOS_HTTP.has(item));
109
+ if (encontrados.length > 0) {
110
+ return [...new Set(encontrados)];
111
+ }
112
+ }
113
+
114
+ return [];
115
+ }
116
+
117
+ function contarChar(texto: string, alvo: string): number {
118
+ return [...texto].filter((char) => char === alvo).length;
119
+ }
120
+
121
+ function atualizarPilhaClasses<T extends { profundidade: number }>(pilha: T[], profundidade: number): void {
122
+ while (pilha.length > 0 && profundidade < pilha[pilha.length - 1]!.profundidade) {
123
+ pilha.pop();
124
+ }
125
+ }
126
+
127
+ function extrairAtributos(linhas: string[], inicio: number): { atributos: string[]; proximoIndice: number } {
128
+ const atributos: string[] = [];
129
+ let indice = inicio;
130
+
131
+ while (indice < linhas.length) {
132
+ const linha = linhas[indice]!.trim();
133
+ if (!linha.startsWith("[")) {
134
+ break;
135
+ }
136
+ let atual = linha;
137
+ let saldo = contarChar(linha, "[") - contarChar(linha, "]");
138
+ while (saldo > 0 && indice + 1 < linhas.length) {
139
+ indice += 1;
140
+ const complemento = linhas[indice]!.trim();
141
+ atual += ` ${complemento}`;
142
+ saldo += contarChar(complemento, "[") - contarChar(complemento, "]");
143
+ }
144
+ atributos.push(atual);
145
+ indice += 1;
146
+ }
147
+
148
+ return { atributos, proximoIndice: indice };
149
+ }
150
+
151
+ export function extrairSimbolosDotnet(codigo: string): SimboloDotnetExtraido[] {
152
+ const simbolos = new Map<string, SimboloDotnetExtraido>();
153
+ const linhas = codigo.split(/\r?\n/);
154
+ const pilhaClasses: Array<{ nome: string; profundidade: number }> = [];
155
+ let classePendente: { nome: string; profundidade: number } | undefined;
156
+ let profundidade = 0;
157
+
158
+ for (let indice = 0; indice < linhas.length; indice += 1) {
159
+ const linhaOriginal = linhas[indice]!;
160
+ const linha = linhaOriginal.trim();
161
+ if (classePendente && linha.startsWith("{")) {
162
+ pilhaClasses.push(classePendente);
163
+ classePendente = undefined;
164
+ }
165
+ if (!linha || linha.startsWith("//")) {
166
+ profundidade += contarChar(linhaOriginal, "{") - contarChar(linhaOriginal, "}");
167
+ atualizarPilhaClasses(pilhaClasses, profundidade);
168
+ continue;
169
+ }
170
+
171
+ const { atributos, proximoIndice } = extrairAtributos(linhas, indice);
172
+ if (atributos.length > 0) {
173
+ indice = proximoIndice;
174
+ }
175
+
176
+ const linhaEfetiva = (linhas[indice] ?? "").trim();
177
+ const classe = linhaEfetiva.match(/\bclass\s+([A-Za-z_]\w*)/);
178
+ if (classe) {
179
+ const entrada = { nome: classe[1]!, profundidade: profundidade + 1 };
180
+ if (linhaEfetiva.includes("{")) {
181
+ pilhaClasses.push(entrada);
182
+ } else {
183
+ classePendente = entrada;
184
+ }
185
+ profundidade += contarChar(linhaEfetiva, "{") - contarChar(linhaEfetiva, "}");
186
+ atualizarPilhaClasses(pilhaClasses, profundidade);
187
+ continue;
188
+ }
189
+
190
+ const metodo = linhaEfetiva.match(/\b(?:public|internal|protected|private)\s+(?:static\s+|async\s+|virtual\s+|override\s+|sealed\s+|partial\s+)*([A-Za-z0-9_<>,.?[\]\s]+)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/);
191
+ if (metodo) {
192
+ const classeAtual = pilhaClasses[pilhaClasses.length - 1];
193
+ const simbolo = classeAtual ? `${classeAtual.nome}.${metodo[2]!}` : metodo[2]!;
194
+ simbolos.set(simbolo, {
195
+ simbolo,
196
+ retorno: metodo[1]!.trim(),
197
+ parametros: metodo[3]!.split(",").flatMap((parametroBruto) => {
198
+ const parametro = parametroBruto.trim();
199
+ if (!parametro) {
200
+ return [];
201
+ }
202
+ const semPadrao = parametro.split("=")[0]?.trim() ?? parametro;
203
+ const partes = semPadrao.split(/\s+/).filter(Boolean);
204
+ if (partes.length < 2) {
205
+ return [];
206
+ }
207
+ return [{
208
+ nome: partes.at(-1)!,
209
+ tipoTexto: partes.slice(0, -1).join(" "),
210
+ obrigatorio: !parametro.includes("="),
211
+ }];
212
+ }),
213
+ });
214
+ profundidade += contarChar(linhaEfetiva, "{") - contarChar(linhaEfetiva, "}");
215
+ atualizarPilhaClasses(pilhaClasses, profundidade);
216
+ continue;
217
+ }
218
+
219
+ const funcaoLocal = linhaEfetiva.match(/\b([A-Za-z0-9_<>,.?[\]\s]+)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*=>/);
220
+ if (funcaoLocal && atributos.length === 0 && pilhaClasses.length === 0) {
221
+ const simbolo = funcaoLocal[2]!;
222
+ simbolos.set(simbolo, {
223
+ simbolo,
224
+ retorno: funcaoLocal[1]!.trim(),
225
+ parametros: [],
226
+ });
227
+ }
228
+
229
+ const funcaoTopo = linhaEfetiva.match(/\b(?:static\s+)?([A-Za-z0-9_<>,.?[\]\s]+)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:\{|$)/);
230
+ if (funcaoTopo && atributos.length === 0 && pilhaClasses.length === 0 && !["if", "for", "while", "switch"].includes(funcaoTopo[2]!)) {
231
+ const simbolo = funcaoTopo[2]!;
232
+ if (!simbolos.has(simbolo)) {
233
+ simbolos.set(simbolo, {
234
+ simbolo,
235
+ retorno: funcaoTopo[1]!.trim(),
236
+ parametros: funcaoTopo[3]!.split(",").flatMap((parametroBruto) => {
237
+ const parametro = parametroBruto.trim();
238
+ if (!parametro) {
239
+ return [];
240
+ }
241
+ const semPadrao = parametro.split("=")[0]?.trim() ?? parametro;
242
+ const partes = semPadrao.split(/\s+/).filter(Boolean);
243
+ if (partes.length < 2) {
244
+ return [];
245
+ }
246
+ return [{
247
+ nome: partes.at(-1)!,
248
+ tipoTexto: partes.slice(0, -1).join(" "),
249
+ obrigatorio: !parametro.includes("="),
250
+ }];
251
+ }),
252
+ });
253
+ }
254
+ }
255
+
256
+ profundidade += contarChar(linhaEfetiva, "{") - contarChar(linhaEfetiva, "}");
257
+ atualizarPilhaClasses(pilhaClasses, profundidade);
258
+ }
259
+
260
+ return [...simbolos.values()];
261
+ }
262
+
263
+ export function extrairRotasDotnet(codigo: string): RotaDotnetExtraida[] {
264
+ const rotas = new Map<string, RotaDotnetExtraida>();
265
+ const linhas = codigo.split(/\r?\n/);
266
+ const pilhaClasses: Array<{ nome: string; profundidade: number; rotaBase?: string; apiController: boolean }> = [];
267
+ let classePendente: { nome: string; profundidade: number; rotaBase?: string; apiController: boolean } | undefined;
268
+ let profundidade = 0;
269
+
270
+ for (let indice = 0; indice < linhas.length; indice += 1) {
271
+ const linhaOriginal = linhas[indice]!;
272
+ const linha = linhaOriginal.trim();
273
+ if (classePendente && linha.startsWith("{")) {
274
+ pilhaClasses.push(classePendente);
275
+ classePendente = undefined;
276
+ }
277
+ if (!linha || linha.startsWith("//")) {
278
+ profundidade += contarChar(linhaOriginal, "{") - contarChar(linhaOriginal, "}");
279
+ atualizarPilhaClasses(pilhaClasses, profundidade);
280
+ continue;
281
+ }
282
+
283
+ const { atributos, proximoIndice } = extrairAtributos(linhas, indice);
284
+ if (atributos.length > 0) {
285
+ indice = proximoIndice;
286
+ }
287
+
288
+ const linhaEfetiva = (linhas[indice] ?? "").trim();
289
+ const classe = linhaEfetiva.match(/\bclass\s+([A-Za-z_]\w*)/);
290
+ if (classe) {
291
+ const rotaBase = atributos
292
+ .map((atributo) => extrairTextoAtributo(atributo))
293
+ .find(Boolean);
294
+ const entrada = {
295
+ nome: classe[1]!,
296
+ profundidade: profundidade + 1,
297
+ rotaBase,
298
+ apiController: atributos.some((atributo) => /\[\s*ApiController\s*\]/i.test(atributo)),
299
+ };
300
+ if (linhaEfetiva.includes("{")) {
301
+ pilhaClasses.push(entrada);
302
+ } else {
303
+ classePendente = entrada;
304
+ }
305
+ profundidade += contarChar(linhaEfetiva, "{") - contarChar(linhaEfetiva, "}");
306
+ atualizarPilhaClasses(pilhaClasses, profundidade);
307
+ continue;
308
+ }
309
+
310
+ const classeAtual = pilhaClasses[pilhaClasses.length - 1];
311
+ const metodo = linhaEfetiva.match(/\b(?:public|internal|protected)\s+(?:async\s+|virtual\s+|override\s+|static\s+)*([A-Za-z0-9_<>,.?[\]\s]+)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/);
312
+ if (metodo && classeAtual) {
313
+ const atributosMetodo = atributos.filter((atributo) => /\[\s*(?:Http|AcceptVerbs)/i.test(atributo));
314
+ for (const atributo of atributosMetodo) {
315
+ const metodos = extrairMetodosAtributo(atributo);
316
+ const rotaMetodo = extrairTextoAtributo(atributo);
317
+ for (const httpMetodo of metodos) {
318
+ const caminho = normalizarCaminhoAspNet(
319
+ juntarCaminho(classeAtual.rotaBase, rotaMetodo),
320
+ classeAtual.nome,
321
+ metodo[2]!,
322
+ );
323
+ const registro: RotaDotnetExtraida = {
324
+ origem: "dotnet",
325
+ metodo: httpMetodo,
326
+ caminho,
327
+ simbolo: `${classeAtual.nome}.${metodo[2]!}`,
328
+ parametros: extrairParametrosRota(caminho, metodo[3] ?? ""),
329
+ retorno: metodo[1]!.trim(),
330
+ };
331
+ rotas.set(`${registro.metodo}:${registro.caminho}:${registro.simbolo}`, registro);
332
+ }
333
+ }
334
+ }
335
+
336
+ for (const match of linhaEfetiva.matchAll(/\b\w+\.Map(Get|Post|Put|Patch|Delete)\(\s*"([^"]+)"\s*,\s*([A-Za-z_][\w.]*)/g)) {
337
+ const httpMetodo = match[1]!.toUpperCase();
338
+ const caminho = normalizarCaminhoAspNet(match[2]!);
339
+ const simbolo = match[3]!;
340
+ const registro: RotaDotnetExtraida = {
341
+ origem: "dotnet",
342
+ metodo: httpMetodo,
343
+ caminho,
344
+ simbolo,
345
+ parametros: extrairParametrosRota(caminho, ""),
346
+ };
347
+ rotas.set(`${registro.metodo}:${registro.caminho}:${registro.simbolo}`, registro);
348
+ }
349
+
350
+ profundidade += contarChar(linhaEfetiva, "{") - contarChar(linhaEfetiva, "}");
351
+ atualizarPilhaClasses(pilhaClasses, profundidade);
352
+ }
353
+
354
+ return [...rotas.values()];
355
+ }