@semacode/cli 1.0.0 → 1.2.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 (48) hide show
  1. package/AGENTS.md +50 -0
  2. package/README.md +15 -2
  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 +546 -0
  7. package/dist/drift.d.ts +17 -2
  8. package/dist/drift.js +516 -5
  9. package/dist/drift.js.map +1 -1
  10. package/dist/importador.d.ts +1 -1
  11. package/dist/importador.js +741 -3
  12. package/dist/importador.js.map +1 -1
  13. package/dist/index.js +962 -86
  14. package/dist/index.js.map +1 -1
  15. package/dist/lua-symbols.d.ts +8 -0
  16. package/dist/lua-symbols.js +68 -0
  17. package/dist/lua-symbols.js.map +1 -0
  18. package/dist/projeto.js +56 -2
  19. package/dist/projeto.js.map +1 -1
  20. package/dist/tipos.d.ts +1 -1
  21. package/docs/AGENT_STARTER.md +34 -6
  22. package/docs/instalacao-e-primeiro-uso.md +18 -9
  23. package/llms-full.txt +34 -0
  24. package/llms.txt +17 -0
  25. package/node_modules/@sema/gerador-dart/package.json +1 -1
  26. package/node_modules/@sema/gerador-lua/dist/index.d.ts +3 -0
  27. package/node_modules/@sema/gerador-lua/dist/index.js +360 -0
  28. package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -0
  29. package/node_modules/@sema/gerador-lua/package.json +7 -0
  30. package/node_modules/@sema/gerador-python/dist/index.js +92 -10
  31. package/node_modules/@sema/gerador-python/dist/index.js.map +1 -1
  32. package/node_modules/@sema/gerador-python/package.json +1 -1
  33. package/node_modules/@sema/gerador-typescript/package.json +1 -1
  34. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +1 -1
  35. package/node_modules/@sema/nucleo/dist/ir/conversor.js +4 -0
  36. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
  37. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +3 -3
  38. package/node_modules/@sema/nucleo/dist/parser/parser.js +2 -0
  39. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
  40. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +2 -2
  41. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +3 -1
  42. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
  43. package/node_modules/@sema/nucleo/package.json +1 -1
  44. package/node_modules/@sema/padroes/dist/index.d.ts +2 -1
  45. package/node_modules/@sema/padroes/dist/index.js +64 -1
  46. package/node_modules/@sema/padroes/dist/index.js.map +1 -1
  47. package/node_modules/@sema/padroes/package.json +1 -1
  48. package/package.json +16 -7
@@ -9,6 +9,7 @@ import { extrairRotasGo, extrairSimbolosGo } from "./go-http.js";
9
9
  import { extrairRotasJava, extrairSimbolosJava } from "./java-http.js";
10
10
  import { extrairParametrosCaminhoFlask, extrairRotasFlaskDecoradas } from "./python-http.js";
11
11
  import { extrairRotasRust, extrairSimbolosRust } from "./rust-http.js";
12
+ import { extrairSimbolosLua } from "./lua-symbols.js";
12
13
  import { extrairRotasTypeScriptHttp, inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http.js";
13
14
  const DIRETORIOS_IGNORADOS = new Set([
14
15
  ".git",
@@ -190,6 +191,7 @@ function mapearTipoPrimitivo(tipo) {
190
191
  const limpo = tipo.trim().replace(/\s+/g, "");
191
192
  const base = limpo
192
193
  .replace(/^Promise<(.*)>$/, "$1")
194
+ .replace(/^Future<(.*)>$/, "$1")
193
195
  .replace(/\|undefined/g, "")
194
196
  .replace(/\|null/g, "")
195
197
  .replace(/\bundefined\|/g, "")
@@ -217,10 +219,22 @@ function mapearTipoPrimitivo(tipo) {
217
219
  if (minusculo === "id" || minusculo.endsWith("id")) {
218
220
  return "Id";
219
221
  }
220
- if (minusculo.includes("[]") || minusculo.startsWith("array<") || minusculo.startsWith("record<") || minusculo.startsWith("list[") || minusculo.startsWith("dict[")) {
222
+ if (minusculo.includes("[]")
223
+ || minusculo.startsWith("array<")
224
+ || minusculo.startsWith("record<")
225
+ || minusculo.startsWith("map<")
226
+ || minusculo.startsWith("list<")
227
+ || minusculo.startsWith("list[")
228
+ || minusculo.startsWith("dict[")) {
221
229
  return "Json";
222
230
  }
223
- if (minusculo === "json" || minusculo === "object" || minusculo === "unknown" || minusculo === "any" || minusculo === "void" || minusculo === "none") {
231
+ if (minusculo === "json"
232
+ || minusculo === "object"
233
+ || minusculo === "unknown"
234
+ || minusculo === "any"
235
+ || minusculo === "dynamic"
236
+ || minusculo === "void"
237
+ || minusculo === "none") {
224
238
  return minusculo === "void" || minusculo === "none" ? "Vazio" : "Json";
225
239
  }
226
240
  return tipo.trim();
@@ -568,6 +582,624 @@ function resolverEscopoImportacaoNextJs(diretorioEntrada) {
568
582
  diretorioEscopo: resolvido,
569
583
  };
570
584
  }
585
+ function normalizarCaminhoImportado(caminhoArquivo) {
586
+ return caminhoArquivo.replace(/\\/g, "/");
587
+ }
588
+ function normalizarSegmentoRotaConsumer(segmento) {
589
+ const opcionalCatchAll = segmento.match(/^\[\[\.\.\.([A-Za-z_]\w*)\]\]$/);
590
+ if (opcionalCatchAll) {
591
+ return `{${opcionalCatchAll[1]}}`;
592
+ }
593
+ const catchAll = segmento.match(/^\[\.\.\.([A-Za-z_]\w*)\]$/);
594
+ if (catchAll) {
595
+ return `{${catchAll[1]}}`;
596
+ }
597
+ const dinamico = segmento.match(/^\[([A-Za-z_]\w*)\]$/);
598
+ if (dinamico) {
599
+ return `{${dinamico[1]}}`;
600
+ }
601
+ return segmento;
602
+ }
603
+ function montarCaminhoRotaConsumer(partes) {
604
+ const filtradas = partes
605
+ .filter((segmento) => segmento && segmento !== "index" && !/^\(.*\)$/.test(segmento) && !segmento.startsWith("@"))
606
+ .map(normalizarSegmentoRotaConsumer);
607
+ return filtradas.length > 0 ? `/${filtradas.join("/")}`.replace(/\/+/g, "/") : "/";
608
+ }
609
+ function resolverEscopoImportacaoFrontendConsumer(diretorioEntrada) {
610
+ const resolvido = path.resolve(diretorioEntrada);
611
+ const partes = path.parse(resolvido);
612
+ const segmentos = resolvido.slice(partes.root.length).split(path.sep).filter(Boolean);
613
+ const procurarSequencia = (sequencia) => segmentos.findIndex((segmento, indice) => sequencia.every((item, deslocamento) => segmentos[indice + deslocamento]?.toLowerCase() === item));
614
+ const montarBase = (indice) => indice <= 0
615
+ ? partes.root
616
+ : path.join(partes.root, ...segmentos.slice(0, indice));
617
+ for (const sequencia of [
618
+ ["src", "pages"],
619
+ ["pages"],
620
+ ["src", "app", "api"],
621
+ ["app", "api"],
622
+ ["src", "app"],
623
+ ["app"],
624
+ ["src", "lib"],
625
+ ["lib"],
626
+ ]) {
627
+ const indice = procurarSequencia(sequencia);
628
+ if (indice >= 0) {
629
+ return {
630
+ baseProjeto: montarBase(indice),
631
+ diretorioEscopo: resolvido,
632
+ };
633
+ }
634
+ }
635
+ return {
636
+ baseProjeto: resolvido,
637
+ diretorioEscopo: resolvido,
638
+ };
639
+ }
640
+ function arquivoEhBridgeNextJsConsumer(relacaoArquivo) {
641
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
642
+ return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
643
+ }
644
+ function arquivoEhBridgeReactViteConsumer(relacaoArquivo) {
645
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
646
+ return /(?:^|\/)(?:src\/)?lib\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
647
+ }
648
+ function arquivoEhBridgeAngularConsumer(relacaoArquivo) {
649
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
650
+ return /(?:^|\/)(?:src\/)?app\/(?:sema_consumer_bridge|sema\/.+)\.(?:ts|js)$/i.test(relacao);
651
+ }
652
+ function arquivoEhSuperficieNextJsConsumer(relacaoArquivo) {
653
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
654
+ return /(?:^|\/)(?:src\/)?app\/(?:(?!api\/).)*?(?:page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/i.test(relacao);
655
+ }
656
+ function arquivoEhSuperficieReactViteConsumer(relacaoArquivo) {
657
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
658
+ return /^(?:src\/)?pages\/.+\.(?:ts|tsx|js|jsx)$/i.test(relacao)
659
+ || /^(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao);
660
+ }
661
+ function arquivoEhRotasReactViteConsumer(relacaoArquivo, codigo) {
662
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
663
+ return /(?:^|\/)(?:src\/)?(?:app\/)?(?:router|routes)\.(?:ts|tsx|js|jsx)$/i.test(relacao)
664
+ || /from\s+["']react-router-dom["']|createBrowserRouter|RouterProvider|useRoutes\s*\(|<Routes\b|<Route\b/.test(codigo ?? "");
665
+ }
666
+ function arquivoEhRotasAngularConsumer(relacaoArquivo) {
667
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
668
+ return /(?:^|\/)(?:src\/)?app(?:\/.+)?\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
669
+ }
670
+ function arquivoEhRotasAngularConsumerRaiz(relacaoArquivo) {
671
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
672
+ return /(?:^|\/)(?:src\/)?app\/[^/]+\.routes\.(?:ts|js)$/i.test(relacao);
673
+ }
674
+ function arquivoEhBridgeFlutterConsumer(relacaoArquivo) {
675
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
676
+ return /(?:^|\/)(?:lib\/)?(?:sema_consumer_bridge|api\/sema_contract_bridge|sema\/.+)\.dart$/i.test(relacao);
677
+ }
678
+ function arquivoEhSuperficieFlutterConsumer(relacaoArquivo) {
679
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
680
+ return /(?:^|\/)(?:lib\/)?(?:screens|pages)\/.+\.dart$/i.test(relacao)
681
+ || /(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao);
682
+ }
683
+ function arquivoEhRotasFlutterConsumer(relacaoArquivo, codigo) {
684
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
685
+ return /(?:^|\/)(?:lib\/)?(?:router|app_router|routes)\.dart$/i.test(relacao)
686
+ || /MaterialApp(?:\.router)?\s*\(|CupertinoApp(?:\.router)?\s*\(|GoRouter\s*\(/.test(codigo ?? "");
687
+ }
688
+ function inferirCaminhoNextJsConsumer(relacaoArquivo) {
689
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
690
+ const segmentos = relacao.split("/");
691
+ const indiceSrcApp = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "app");
692
+ const indiceApp = segmentos.findIndex((segmento) => segmento === "app");
693
+ const inicioApp = indiceSrcApp >= 0 ? indiceSrcApp + 2 : indiceApp >= 0 ? indiceApp + 1 : -1;
694
+ if (inicioApp < 0) {
695
+ return undefined;
696
+ }
697
+ const arquivoFinal = segmentos.at(-1) ?? "";
698
+ const tipoArquivo = arquivoFinal.match(/^(page|layout|loading|error)\.(?:ts|tsx|js|jsx)$/)?.[1];
699
+ if (!tipoArquivo) {
700
+ return undefined;
701
+ }
702
+ const caminhoAteArquivo = segmentos.slice(inicioApp, -1);
703
+ if (caminhoAteArquivo[0] === "api") {
704
+ return undefined;
705
+ }
706
+ const partes = caminhoAteArquivo
707
+ .filter((segmento) => segmento);
708
+ const caminho = montarCaminhoRotaConsumer(partes);
709
+ return {
710
+ caminho,
711
+ arquivo: relacao,
712
+ tipoArquivo,
713
+ };
714
+ }
715
+ function inferirCaminhoReactViteConsumer(relacaoArquivo) {
716
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
717
+ if (!arquivoEhSuperficieReactViteConsumer(relacao)) {
718
+ return undefined;
719
+ }
720
+ if (/(?:^|\/)(?:src\/)?App\.(?:ts|tsx|js|jsx)$/i.test(relacao)) {
721
+ return {
722
+ caminho: "/",
723
+ arquivo: relacao,
724
+ tipoArquivo: "app",
725
+ };
726
+ }
727
+ const segmentos = relacao.split("/");
728
+ const indiceSrcPages = segmentos.findIndex((segmento, indice) => segmento === "src" && segmentos[indice + 1] === "pages");
729
+ const indicePages = segmentos.findIndex((segmento) => segmento === "pages");
730
+ const inicioPages = indiceSrcPages >= 0 ? indiceSrcPages + 2 : indicePages >= 0 ? indicePages + 1 : -1;
731
+ if (inicioPages < 0) {
732
+ return undefined;
733
+ }
734
+ const arquivoFinal = segmentos.at(-1) ?? "";
735
+ const nomeBase = arquivoFinal.replace(/\.(?:ts|tsx|js|jsx)$/i, "");
736
+ const caminho = montarCaminhoRotaConsumer([...segmentos.slice(inicioPages, -1), nomeBase]);
737
+ return {
738
+ caminho,
739
+ arquivo: relacao,
740
+ tipoArquivo: "page",
741
+ };
742
+ }
743
+ function inferirCaminhoFlutterConsumer(relacaoArquivo) {
744
+ const relacao = normalizarCaminhoImportado(relacaoArquivo);
745
+ if (!arquivoEhSuperficieFlutterConsumer(relacao)) {
746
+ return undefined;
747
+ }
748
+ if (/(?:^|\/)(?:lib\/)?main\.dart$/i.test(relacao)) {
749
+ return {
750
+ caminho: "/",
751
+ arquivo: relacao,
752
+ tipoArquivo: "app",
753
+ };
754
+ }
755
+ const segmentos = relacao.split("/");
756
+ const indiceLibScreens = segmentos.findIndex((segmento, indice) => segmento === "lib" && ["screens", "pages"].includes(segmentos[indice + 1] ?? ""));
757
+ const indiceScreens = segmentos.findIndex((segmento) => segmento === "screens" || segmento === "pages");
758
+ const inicio = indiceLibScreens >= 0 ? indiceLibScreens + 2 : indiceScreens >= 0 ? indiceScreens + 1 : -1;
759
+ if (inicio < 0) {
760
+ return undefined;
761
+ }
762
+ const arquivoFinal = segmentos.at(-1) ?? "";
763
+ const nomeBase = arquivoFinal
764
+ .replace(/\.(?:dart)$/i, "")
765
+ .replace(/_(screen|page)$/i, "");
766
+ return {
767
+ caminho: montarCaminhoRotaConsumer([...segmentos.slice(inicio, -1), nomeBase]),
768
+ arquivo: relacao,
769
+ tipoArquivo: "screen",
770
+ };
771
+ }
772
+ function normalizarRotaDeclaradaConsumer(caminhoCru, prefixo = "/") {
773
+ const partesPrefixo = prefixo.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
774
+ const partesCaminho = (caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
775
+ return montarCaminhoRotaConsumer([...partesPrefixo, ...partesCaminho]);
776
+ }
777
+ function resolverImportRelativoTypeScript(relacaoArquivoBase, especificador) {
778
+ if (!especificador.startsWith(".")) {
779
+ return undefined;
780
+ }
781
+ const baseDir = path.posix.dirname(normalizarCaminhoImportado(relacaoArquivoBase));
782
+ for (const sufixo of ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"]) {
783
+ const candidato = path.posix.normalize(path.posix.join(baseDir, `${especificador}${sufixo}`));
784
+ if (!/\.(?:ts|tsx|js|jsx)$/i.test(candidato)) {
785
+ continue;
786
+ }
787
+ return candidato;
788
+ }
789
+ return undefined;
790
+ }
791
+ function extrairImportsTypeScriptConsumer(relacaoArquivo, codigo) {
792
+ const imports = new Map();
793
+ for (const match of codigo.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*["']([^"']+)["']/g)) {
794
+ const moduloImportado = match[2];
795
+ const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, moduloImportado);
796
+ if (!relacaoImportada) {
797
+ continue;
798
+ }
799
+ for (const bruto of match[1].split(",")) {
800
+ const normalizado = bruto.trim();
801
+ if (!normalizado) {
802
+ continue;
803
+ }
804
+ const local = normalizado.split(/\s+as\s+/i).at(-1)?.trim();
805
+ if (local) {
806
+ imports.set(local, relacaoImportada);
807
+ }
808
+ }
809
+ }
810
+ for (const match of codigo.matchAll(/import\s+([A-Za-z_]\w*)\s+from\s*["']([^"']+)["']/g)) {
811
+ const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2]);
812
+ const local = match[1]?.trim();
813
+ if (relacaoImportada && local) {
814
+ imports.set(local, relacaoImportada);
815
+ }
816
+ }
817
+ return imports;
818
+ }
819
+ function extrairRotasReactViteConsumer(relacaoArquivo, codigo) {
820
+ const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
821
+ const rotas = new Map();
822
+ const registrar = (caminhoCru, componente) => {
823
+ const caminho = normalizarRotaDeclaradaConsumer(caminhoCru);
824
+ const chave = `${caminho}:${normalizarCaminhoImportado(relacaoArquivo)}:${componente ?? "router"}`;
825
+ rotas.set(chave, {
826
+ caminho,
827
+ arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
828
+ arquivoComponente: componente ? imports.get(componente) : undefined,
829
+ });
830
+ };
831
+ 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)) {
832
+ const caminhoCru = match[1] ?? "";
833
+ const componente = match[2] ?? match[3];
834
+ registrar(caminhoCru, componente);
835
+ }
836
+ 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)) {
837
+ const caminhoCru = match[2] ? "" : (match[1] ?? "");
838
+ const componente = match[3] ?? match[4];
839
+ registrar(caminhoCru, componente);
840
+ }
841
+ return [...rotas.values()];
842
+ }
843
+ function extrairRotasAngularConsumerDiretas(relacaoArquivo, codigo, prefixo = "/") {
844
+ const imports = extrairImportsTypeScriptConsumer(relacaoArquivo, codigo);
845
+ const rotas = [];
846
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?component\s*:\s*([A-Za-z_]\w*)/g)) {
847
+ const caminhoCru = (match[1] ?? "").trim();
848
+ const componente = match[2];
849
+ rotas.push({
850
+ caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
851
+ arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
852
+ arquivoComponente: imports.get(componente),
853
+ });
854
+ }
855
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,320}?loadComponent\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
856
+ const caminhoCru = (match[1] ?? "").trim();
857
+ const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2] ?? "");
858
+ rotas.push({
859
+ caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
860
+ arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
861
+ arquivoComponente: relacaoImportada,
862
+ });
863
+ }
864
+ for (const match of codigo.matchAll(/path\s*:\s*["'`]([^"'`]*)["'`][\s\S]{0,360}?loadChildren\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g)) {
865
+ const caminhoCru = (match[1] ?? "").trim();
866
+ const relacaoImportada = resolverImportRelativoTypeScript(relacaoArquivo, match[2] ?? "");
867
+ rotas.push({
868
+ caminho: normalizarRotaDeclaradaConsumer(caminhoCru, prefixo),
869
+ arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
870
+ arquivoRotasFilhas: relacaoImportada,
871
+ });
872
+ }
873
+ return rotas;
874
+ }
875
+ async function extrairRotasAngularConsumer(baseProjeto, relacaoArquivo, prefixo = "/", visitados = new Set()) {
876
+ const relacaoNormalizada = normalizarCaminhoImportado(relacaoArquivo);
877
+ if (visitados.has(relacaoNormalizada)) {
878
+ return [];
879
+ }
880
+ visitados.add(relacaoNormalizada);
881
+ const caminhoAbsoluto = path.join(baseProjeto, relacaoNormalizada);
882
+ let codigo = "";
883
+ try {
884
+ codigo = await readFile(caminhoAbsoluto, "utf8");
885
+ }
886
+ catch {
887
+ return [];
888
+ }
889
+ const diretas = extrairRotasAngularConsumerDiretas(relacaoNormalizada, codigo, prefixo);
890
+ const agregadas = [...diretas];
891
+ for (const rota of diretas) {
892
+ if (!rota.arquivoRotasFilhas) {
893
+ continue;
894
+ }
895
+ agregadas.push(...await extrairRotasAngularConsumer(baseProjeto, rota.arquivoRotasFilhas, rota.caminho, visitados));
896
+ }
897
+ return agregadas;
898
+ }
899
+ function normalizarRotaDeclaradaFlutter(caminhoCru) {
900
+ return montarCaminhoRotaConsumer((caminhoCru ?? "").trim().replace(/^\/+|\/+$/g, "").split("/").filter(Boolean));
901
+ }
902
+ function extrairRotasFlutterConsumer(relacaoArquivo, codigo) {
903
+ const rotas = new Map();
904
+ const registrar = (caminhoCru) => {
905
+ const caminho = normalizarRotaDeclaradaFlutter(caminhoCru);
906
+ rotas.set(`${caminho}:${normalizarCaminhoImportado(relacaoArquivo)}`, {
907
+ caminho,
908
+ arquivoRotas: normalizarCaminhoImportado(relacaoArquivo),
909
+ });
910
+ };
911
+ for (const match of codigo.matchAll(/GoRoute\s*\([\s\S]{0,220}?path\s*:\s*["'`]([^"'`]+)["'`]/g)) {
912
+ registrar(match[1] ?? "");
913
+ }
914
+ for (const match of codigo.matchAll(/["'`]([^"'`]+)["'`]\s*:\s*\([^)]*\)\s*=>/g)) {
915
+ registrar(match[1] ?? "");
916
+ }
917
+ if (/home\s*:\s*(?:const\s+)?[A-Za-z_]\w*\(/.test(codigo)) {
918
+ registrar("/");
919
+ }
920
+ return [...rotas.values()];
921
+ }
922
+ async function carregarContextosBridgeConsumer(baseProjeto, arquivosBridge) {
923
+ return Promise.all(arquivosBridge.map(async (arquivo) => {
924
+ const texto = await readFile(arquivo, "utf8");
925
+ const scriptKind = arquivo.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
926
+ return {
927
+ sourceFile: ts.createSourceFile(arquivo, texto, ts.ScriptTarget.Latest, true, scriptKind),
928
+ texto,
929
+ relacao: path.relative(baseProjeto, arquivo),
930
+ };
931
+ }));
932
+ }
933
+ function extrairTasksBridgeConsumer(baseProjeto, contextosBridge) {
934
+ const tiposGlobais = consolidarTiposTs(contextosBridge);
935
+ const entitiesRef = new Set();
936
+ const enumsRef = new Set();
937
+ const tasks = [];
938
+ for (const contexto of contextosBridge) {
939
+ contexto.sourceFile.forEachChild((node) => {
940
+ if (ts.isFunctionDeclaration(node) && node.name && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
941
+ const nome = node.name.text;
942
+ const input = node.parameters.flatMap((parametro) => expandirCamposTs(parametro.name.getText(contexto.sourceFile), parametro.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, !parametro.questionToken));
943
+ const output = node.type?.getText(contexto.sourceFile) && mapearTipoPrimitivo(node.type.getText(contexto.sourceFile)) === "Vazio"
944
+ ? []
945
+ : deduplicarCampos(expandirCamposTs("resultado", node.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, false));
946
+ tasks.push({
947
+ nome: nomeTaskBridgeConsumer(nome),
948
+ resumo: `Task consumer importada automaticamente de ${contexto.relacao}#${nome}.`,
949
+ input: deduplicarCampos(input),
950
+ output,
951
+ errors: node.body ? extrairErrosTs(node.body, contexto.sourceFile) : [],
952
+ effects: node.body ? descreverEfeitosPorHeuristica(node.body.getText(contexto.sourceFile)) : [],
953
+ impl: { ts: caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), nome) },
954
+ vinculos: deduplicarVinculos([
955
+ { tipo: "arquivo", valor: normalizarCaminhoImportado(contexto.relacao) },
956
+ { tipo: "simbolo", valor: caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), nome) },
957
+ ]),
958
+ origemArquivo: contexto.relacao,
959
+ origemSimbolo: nome,
960
+ });
961
+ }
962
+ if (ts.isClassDeclaration(node) && node.name && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
963
+ const nomeClasse = node.name.text;
964
+ for (const member of node.members) {
965
+ if (!ts.isMethodDeclaration(member) || !member.name || !member.body) {
966
+ continue;
967
+ }
968
+ if (member.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword || modifier.kind === ts.SyntaxKind.ProtectedKeyword)) {
969
+ continue;
970
+ }
971
+ const nomeMetodo = member.name.getText(contexto.sourceFile);
972
+ if (nomeMetodo === "constructor") {
973
+ continue;
974
+ }
975
+ const input = member.parameters.flatMap((parametro) => expandirCamposTs(parametro.name.getText(contexto.sourceFile), parametro.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, !parametro.questionToken));
976
+ const output = member.type?.getText(contexto.sourceFile) && mapearTipoPrimitivo(member.type.getText(contexto.sourceFile)) === "Vazio"
977
+ ? []
978
+ : deduplicarCampos(expandirCamposTs("resultado", member.type?.getText(contexto.sourceFile), tiposGlobais, entitiesRef, enumsRef, false));
979
+ const caminhoSimbolo = caminhoImplTs(baseProjeto, path.join(baseProjeto, contexto.relacao), `${nomeClasse}.${nomeMetodo}`);
980
+ tasks.push({
981
+ nome: nomeTaskBridgeConsumer(nomeMetodo),
982
+ resumo: `Task consumer importada automaticamente de ${contexto.relacao}#${nomeClasse}.${nomeMetodo}.`,
983
+ input: deduplicarCampos(input),
984
+ output,
985
+ errors: extrairErrosTs(member.body, contexto.sourceFile),
986
+ effects: descreverEfeitosPorHeuristica(member.body.getText(contexto.sourceFile)),
987
+ impl: { ts: caminhoSimbolo },
988
+ vinculos: deduplicarVinculos([
989
+ { tipo: "arquivo", valor: normalizarCaminhoImportado(contexto.relacao) },
990
+ { tipo: "simbolo", valor: caminhoSimbolo },
991
+ ]),
992
+ origemArquivo: contexto.relacao,
993
+ origemSimbolo: `${nomeClasse}.${nomeMetodo}`,
994
+ });
995
+ }
996
+ }
997
+ });
998
+ }
999
+ const { entities, enums } = criarEntidadesReferenciadas(tiposGlobais, entitiesRef, enumsRef);
1000
+ return {
1001
+ tasks,
1002
+ entities,
1003
+ enums,
1004
+ };
1005
+ }
1006
+ function montarVinculosSuperficiesConsumer(superficies) {
1007
+ return deduplicarVinculos(superficies.flatMap((superficie) => [
1008
+ { tipo: "superficie", valor: superficie.caminho },
1009
+ { tipo: "arquivo", valor: normalizarCaminhoImportado(superficie.arquivo) },
1010
+ ]));
1011
+ }
1012
+ async function importarConsumerBase(diretorio, namespaceBase, descricaoFramework, ehBridge, coletarSuperficies) {
1013
+ const escopo = resolverEscopoImportacaoFrontendConsumer(diretorio);
1014
+ const arquivos = await listarArquivosRecursivos(escopo.baseProjeto, [".ts", ".tsx", ".js", ".jsx"]);
1015
+ const arquivosBridge = arquivos.filter((arquivo) => ehBridge(path.relative(escopo.baseProjeto, arquivo)));
1016
+ const contextosBridge = await carregarContextosBridgeConsumer(escopo.baseProjeto, arquivosBridge);
1017
+ const { tasks, entities, enums } = extrairTasksBridgeConsumer(escopo.baseProjeto, contextosBridge);
1018
+ const superficiesImportadas = await coletarSuperficies(escopo.baseProjeto, arquivos);
1019
+ const superficies = montarVinculosSuperficiesConsumer(superficiesImportadas);
1020
+ if (tasks.length === 0 && superficies.length === 0) {
1021
+ return [];
1022
+ }
1023
+ const nomeModulo = namespaceBase.endsWith(".consumer")
1024
+ ? namespaceBase
1025
+ : `${namespaceBase}.consumer`;
1026
+ return [{
1027
+ nome: nomeModulo,
1028
+ resumo: `Rascunho Sema importado automaticamente do consumer ${descricaoFramework} em ${escopo.baseProjeto}.`,
1029
+ tasks: deduplicarTarefas(tasks),
1030
+ routes: [],
1031
+ entities,
1032
+ enums,
1033
+ vinculos: superficies,
1034
+ }];
1035
+ }
1036
+ async function coletarSuperficiesNextJsConsumer(baseProjeto, arquivos) {
1037
+ return arquivos
1038
+ .map((arquivo) => inferirCaminhoNextJsConsumer(path.relative(baseProjeto, arquivo)))
1039
+ .filter((item) => Boolean(item));
1040
+ }
1041
+ async function coletarSuperficiesReactViteConsumer(baseProjeto, arquivos) {
1042
+ const superficies = [];
1043
+ for (const arquivo of arquivos) {
1044
+ const relacao = path.relative(baseProjeto, arquivo);
1045
+ const codigo = await readFile(arquivo, "utf8");
1046
+ if (arquivoEhRotasReactViteConsumer(relacao, codigo)) {
1047
+ for (const rota of extrairRotasReactViteConsumer(relacao, codigo)) {
1048
+ superficies.push({
1049
+ caminho: rota.caminho,
1050
+ arquivo: rota.arquivoRotas,
1051
+ tipoArquivo: "router",
1052
+ });
1053
+ if (rota.arquivoComponente) {
1054
+ superficies.push({
1055
+ caminho: rota.caminho,
1056
+ arquivo: rota.arquivoComponente,
1057
+ tipoArquivo: "page",
1058
+ });
1059
+ }
1060
+ }
1061
+ }
1062
+ }
1063
+ for (const arquivo of arquivos) {
1064
+ const superficie = inferirCaminhoReactViteConsumer(path.relative(baseProjeto, arquivo));
1065
+ if (superficie) {
1066
+ superficies.push(superficie);
1067
+ }
1068
+ }
1069
+ return superficies;
1070
+ }
1071
+ async function coletarSuperficiesAngularConsumer(baseProjeto, arquivos) {
1072
+ const superficies = [];
1073
+ const arquivosRotas = arquivos.filter((arquivo) => arquivoEhRotasAngularConsumer(path.relative(baseProjeto, arquivo)));
1074
+ const arquivosRaiz = arquivosRotas.filter((arquivo) => arquivoEhRotasAngularConsumerRaiz(path.relative(baseProjeto, arquivo)));
1075
+ const pontosEntrada = arquivosRaiz.length > 0 ? arquivosRaiz : arquivosRotas;
1076
+ for (const arquivoRotas of pontosEntrada) {
1077
+ const relacao = path.relative(baseProjeto, arquivoRotas);
1078
+ for (const rota of await extrairRotasAngularConsumer(baseProjeto, relacao)) {
1079
+ superficies.push({
1080
+ caminho: rota.caminho,
1081
+ arquivo: rota.arquivoRotas,
1082
+ tipoArquivo: "routes",
1083
+ });
1084
+ if (rota.arquivoComponente) {
1085
+ superficies.push({
1086
+ caminho: rota.caminho,
1087
+ arquivo: rota.arquivoComponente,
1088
+ tipoArquivo: "component",
1089
+ });
1090
+ }
1091
+ }
1092
+ }
1093
+ return superficies;
1094
+ }
1095
+ async function importarNextJsConsumerBase(diretorio, namespaceBase) {
1096
+ return importarConsumerBase(diretorio, namespaceBase, "Next.js", arquivoEhBridgeNextJsConsumer, coletarSuperficiesNextJsConsumer);
1097
+ }
1098
+ async function importarReactViteConsumerBase(diretorio, namespaceBase) {
1099
+ return importarConsumerBase(diretorio, namespaceBase, "React/Vite", arquivoEhBridgeReactViteConsumer, coletarSuperficiesReactViteConsumer);
1100
+ }
1101
+ async function importarAngularConsumerBase(diretorio, namespaceBase) {
1102
+ return importarConsumerBase(diretorio, namespaceBase, "Angular", arquivoEhBridgeAngularConsumer, coletarSuperficiesAngularConsumer);
1103
+ }
1104
+ async function extrairTasksBridgeFlutterConsumer(baseProjeto, arquivosBridge) {
1105
+ const tasks = [];
1106
+ for (const arquivo of arquivosBridge) {
1107
+ const texto = await readFile(arquivo, "utf8");
1108
+ const relacao = path.relative(baseProjeto, arquivo);
1109
+ for (const match of texto.matchAll(/(?:Future<([^\n]+)>|([\w?<>.,\s]+))\s+(\w+)\(([^)]*)\)\s*(?:async\s*)?\{/g)) {
1110
+ const retorno = (match[1] ?? match[2] ?? "").trim();
1111
+ const nome = match[3];
1112
+ if (["build", "toString", "hashCode"].includes(nome)) {
1113
+ continue;
1114
+ }
1115
+ const parametros = match[4];
1116
+ const input = parametros
1117
+ .split(",")
1118
+ .map((item) => item.trim())
1119
+ .filter(Boolean)
1120
+ .map((item) => item.replace(/^(required|final)\s+/g, ""))
1121
+ .map((item) => {
1122
+ const partes = item.split(/\s+/).filter(Boolean);
1123
+ const nomeParametro = partes.at(-1) ?? "arg";
1124
+ const tipoParametro = partes.slice(0, -1).join(" ");
1125
+ return {
1126
+ nome: paraSnakeCase(nomeParametro),
1127
+ tipo: mapearTipoPrimitivo(tipoParametro || "Json"),
1128
+ obrigatorio: !/\?/.test(tipoParametro),
1129
+ };
1130
+ });
1131
+ const caminhoSimbolo = caminhoImplDart(baseProjeto, arquivo, nome);
1132
+ tasks.push({
1133
+ nome: nomeTaskBridgeConsumer(nome),
1134
+ resumo: `Task consumer importada automaticamente de ${relacao}#${nome}.`,
1135
+ input,
1136
+ output: retorno && mapearTipoPrimitivo(retorno) === "Vazio"
1137
+ ? []
1138
+ : [{ nome: "resultado", tipo: mapearTipoPrimitivo(retorno || "Json"), obrigatorio: false }],
1139
+ errors: [],
1140
+ effects: descreverEfeitosPorHeuristica(texto),
1141
+ impl: { dart: caminhoSimbolo },
1142
+ vinculos: deduplicarVinculos([
1143
+ { tipo: "arquivo", valor: normalizarCaminhoImportado(relacao) },
1144
+ { tipo: "simbolo", valor: caminhoSimbolo },
1145
+ ]),
1146
+ origemArquivo: relacao,
1147
+ origemSimbolo: nome,
1148
+ });
1149
+ }
1150
+ }
1151
+ return tasks;
1152
+ }
1153
+ async function coletarSuperficiesFlutterConsumer(baseProjeto, arquivos) {
1154
+ const superficies = [];
1155
+ for (const arquivo of arquivos) {
1156
+ const relacao = path.relative(baseProjeto, arquivo);
1157
+ const codigo = await readFile(arquivo, "utf8");
1158
+ if (arquivoEhRotasFlutterConsumer(relacao, codigo)) {
1159
+ for (const rota of extrairRotasFlutterConsumer(relacao, codigo)) {
1160
+ superficies.push({
1161
+ caminho: rota.caminho,
1162
+ arquivo: rota.arquivoRotas,
1163
+ tipoArquivo: "router",
1164
+ });
1165
+ }
1166
+ }
1167
+ }
1168
+ for (const arquivo of arquivos) {
1169
+ const superficie = inferirCaminhoFlutterConsumer(path.relative(baseProjeto, arquivo));
1170
+ if (superficie) {
1171
+ superficies.push(superficie);
1172
+ }
1173
+ }
1174
+ return superficies;
1175
+ }
1176
+ async function importarFlutterConsumerBase(diretorio, namespaceBase) {
1177
+ const escopo = resolverEscopoImportacaoFrontendConsumer(diretorio);
1178
+ const arquivos = (await listarArquivosRecursivos(escopo.baseProjeto, [".dart"]))
1179
+ .filter((arquivo) => !arquivo.endsWith(".g.dart") && !arquivo.endsWith(".freezed.dart"));
1180
+ const arquivosBridge = arquivos.filter((arquivo) => arquivoEhBridgeFlutterConsumer(path.relative(escopo.baseProjeto, arquivo)));
1181
+ const tasks = await extrairTasksBridgeFlutterConsumer(escopo.baseProjeto, arquivosBridge);
1182
+ const superficiesImportadas = await coletarSuperficiesFlutterConsumer(escopo.baseProjeto, arquivos);
1183
+ const superficies = montarVinculosSuperficiesConsumer(superficiesImportadas);
1184
+ if (tasks.length === 0 && superficies.length === 0) {
1185
+ return [];
1186
+ }
1187
+ const nomeModulo = namespaceBase.endsWith(".consumer")
1188
+ ? namespaceBase
1189
+ : `${namespaceBase}.consumer`;
1190
+ return [{
1191
+ nome: nomeModulo,
1192
+ resumo: `Rascunho Sema importado automaticamente do consumer Flutter em ${escopo.baseProjeto}.`,
1193
+ tasks: deduplicarTarefas(tasks),
1194
+ routes: [],
1195
+ entities: [],
1196
+ enums: [],
1197
+ vinculos: superficies,
1198
+ }];
1199
+ }
1200
+ function nomeTaskBridgeConsumer(nome) {
1201
+ return paraSnakeCase(nome.replace(/^sema/, "")) || paraSnakeCase(nome) || "task_consumer";
1202
+ }
571
1203
  function extrairChamadaServiceTs(node) {
572
1204
  let encontrado;
573
1205
  const visitar = (atual) => {
@@ -614,6 +1246,16 @@ function deduplicarEfeitos(effects) {
614
1246
  }
615
1247
  return [...mapa.values()];
616
1248
  }
1249
+ function deduplicarVinculos(vinculos) {
1250
+ const mapa = new Map();
1251
+ for (const vinculo of vinculos) {
1252
+ const chave = `${vinculo.tipo}:${vinculo.valor}`;
1253
+ if (!mapa.has(chave)) {
1254
+ mapa.set(chave, vinculo);
1255
+ }
1256
+ }
1257
+ return [...mapa.values()];
1258
+ }
617
1259
  function deduplicarEntidades(entities) {
618
1260
  const mapa = new Map();
619
1261
  for (const entity of entities) {
@@ -644,6 +1286,7 @@ function deduplicarTarefas(tasks) {
644
1286
  existente.output = deduplicarCampos([...existente.output, ...task.output]);
645
1287
  existente.errors = deduplicarErros([...existente.errors, ...task.errors]);
646
1288
  existente.effects = deduplicarEfeitos([...existente.effects, ...task.effects]);
1289
+ existente.vinculos = deduplicarVinculos([...(existente.vinculos ?? []), ...(task.vinculos ?? [])]);
647
1290
  }
648
1291
  return [...mapa.values()];
649
1292
  }
@@ -714,6 +1357,7 @@ function renderizarImpl(impl, indentacao = " ") {
714
1357
  ...(impl.ts ? [`${indentacao} ts: ${impl.ts}`] : []),
715
1358
  ...(impl.py ? [`${indentacao} py: ${impl.py}`] : []),
716
1359
  ...(impl.dart ? [`${indentacao} dart: ${impl.dart}`] : []),
1360
+ ...(impl.lua ? [`${indentacao} lua: ${impl.lua}`] : []),
717
1361
  ...(impl.cs ? [`${indentacao} cs: ${impl.cs}`] : []),
718
1362
  ...(impl.java ? [`${indentacao} java: ${impl.java}`] : []),
719
1363
  ...(impl.go ? [`${indentacao} go: ${impl.go}`] : []),
@@ -723,6 +1367,26 @@ function renderizarImpl(impl, indentacao = " ") {
723
1367
  "",
724
1368
  ];
725
1369
  }
1370
+ function renderizarValorVinculo(vinculo) {
1371
+ if (vinculo.tipo === "simbolo") {
1372
+ return vinculo.valor;
1373
+ }
1374
+ if (vinculo.tipo === "arquivo" || vinculo.valor.includes("/") || vinculo.valor.includes("\\") || vinculo.valor.includes("{")) {
1375
+ return `"${escaparTexto(vinculo.valor)}"`;
1376
+ }
1377
+ return vinculo.valor;
1378
+ }
1379
+ function renderizarVinculos(vinculos, indentacao = " ") {
1380
+ if (!vinculos || vinculos.length === 0) {
1381
+ return [];
1382
+ }
1383
+ return [
1384
+ `${indentacao}vinculos {`,
1385
+ ...vinculos.map((vinculo) => `${indentacao} ${vinculo.tipo}: ${renderizarValorVinculo(vinculo)}`),
1386
+ `${indentacao}}`,
1387
+ "",
1388
+ ];
1389
+ }
726
1390
  function renderizarTask(task) {
727
1391
  const linhas = [
728
1392
  ` task ${task.nome} {`,
@@ -734,6 +1398,7 @@ function renderizarTask(task) {
734
1398
  ...renderizarCampos("output", task.output, " ", true),
735
1399
  ...renderizarEffects(task.effects, " "),
736
1400
  ...renderizarImpl(task.impl, " "),
1401
+ ...renderizarVinculos(task.vinculos, " "),
737
1402
  ...renderizarErrors(task.errors, " "),
738
1403
  ];
739
1404
  linhas.push(" guarantees {");
@@ -791,6 +1456,7 @@ function moduloParaCodigo(modulo) {
791
1456
  ` resumo: "${escaparTexto(modulo.resumo)}"`,
792
1457
  " }",
793
1458
  "",
1459
+ ...renderizarVinculos(modulo.vinculos, " "),
794
1460
  ...modulo.enums.flatMap(renderizarEnum),
795
1461
  ...modulo.entities.flatMap(renderizarEntidade),
796
1462
  ...modulo.tasks.flatMap(renderizarTask),
@@ -1369,6 +2035,14 @@ function caminhoImplPython(diretorioBase, arquivo, simbolo) {
1369
2035
  function caminhoImplDart(diretorioBase, arquivo, simbolo) {
1370
2036
  return caminhoImplGenerico(diretorioBase, arquivo, simbolo);
1371
2037
  }
2038
+ function caminhoImplLua(diretorioBase, arquivo, simbolo) {
2039
+ const caminhoBase = caminhoImplGenerico(diretorioBase, arquivo, "");
2040
+ const ultimoSegmentoArquivo = caminhoBase.split(".").filter(Boolean).at(-1);
2041
+ const simboloNormalizado = ultimoSegmentoArquivo && simbolo.startsWith(`${ultimoSegmentoArquivo}.`)
2042
+ ? simbolo.slice(ultimoSegmentoArquivo.length + 1)
2043
+ : simbolo;
2044
+ return [...caminhoBase.split(".").filter(Boolean), simboloNormalizado].filter(Boolean).join(".");
2045
+ }
1372
2046
  function dividirParametrosPython(parametros) {
1373
2047
  const partes = [];
1374
2048
  let atual = "";
@@ -1672,7 +2346,54 @@ async function importarDartBase(diretorio, namespaceBase) {
1672
2346
  }
1673
2347
  return modulos;
1674
2348
  }
1675
- function criarModuloImportadoSimples(nome, resumo, tasks, routes = []) {
2349
+ async function importarLuaBase(diretorio, namespaceBase) {
2350
+ const arquivos = (await listarArquivosRecursivos(diretorio, [".lua"]))
2351
+ .filter((arquivo) => !/(^|[\\/])(spec|specs|test|tests)([\\/]|$)/i.test(arquivo))
2352
+ .filter((arquivo) => !/[_-](spec|test)\.lua$/i.test(arquivo));
2353
+ const modulos = [];
2354
+ for (const arquivo of arquivos) {
2355
+ const texto = await readFile(arquivo, "utf8");
2356
+ const relacao = path.relative(diretorio, arquivo);
2357
+ const contextoSegmentos = inferirContextoPorArquivo(relacao, { preservarUltimo: true });
2358
+ const nomeModulo = [namespaceBase, ...contextoSegmentos].join(".");
2359
+ const tasks = [];
2360
+ for (const simbolo of extrairSimbolosLua(texto)) {
2361
+ const nomeBase = simbolo.simbolo.split(".").at(-1) ?? simbolo.simbolo;
2362
+ const input = simbolo.parametros.map((parametro) => ({
2363
+ nome: normalizarNomeCampoImportado(parametro.nome),
2364
+ tipo: "Json",
2365
+ obrigatorio: parametro.obrigatorio,
2366
+ }));
2367
+ const output = /\breturn\b/.test(texto)
2368
+ ? [{ nome: "resultado", tipo: "Json", obrigatorio: false }]
2369
+ : [];
2370
+ tasks.push({
2371
+ nome: paraSnakeCase(nomeBase),
2372
+ resumo: `Task importada automaticamente de ${relacao}#${simbolo.simbolo}.`,
2373
+ input,
2374
+ output,
2375
+ errors: [],
2376
+ effects: descreverEfeitosPorHeuristica(texto),
2377
+ impl: { lua: caminhoImplLua(diretorio, arquivo, simbolo.simbolo) },
2378
+ origemArquivo: relacao,
2379
+ origemSimbolo: simbolo.simbolo,
2380
+ });
2381
+ }
2382
+ if (tasks.length === 0) {
2383
+ continue;
2384
+ }
2385
+ modulos.push({
2386
+ nome: nomeModulo,
2387
+ resumo: `Rascunho Sema importado automaticamente de ${relacao}.`,
2388
+ tasks: deduplicarTarefas(tasks),
2389
+ routes: [],
2390
+ entities: [],
2391
+ enums: [],
2392
+ });
2393
+ }
2394
+ return modulos;
2395
+ }
2396
+ function criarModuloImportadoSimples(nome, resumo, tasks, routes = [], vinculos = []) {
1676
2397
  sincronizarRotasComTasks(routes, tasks);
1677
2398
  return {
1678
2399
  nome,
@@ -1681,6 +2402,7 @@ function criarModuloImportadoSimples(nome, resumo, tasks, routes = []) {
1681
2402
  routes: deduplicarRotas(routes),
1682
2403
  entities: [],
1683
2404
  enums: [],
2405
+ vinculos: deduplicarVinculos(vinculos),
1684
2406
  };
1685
2407
  }
1686
2408
  function acumularModuloImportado(modulos, modulo) {
@@ -1693,6 +2415,7 @@ function acumularModuloImportado(modulos, modulo) {
1693
2415
  existente.routes = deduplicarRotas([...existente.routes, ...modulo.routes]);
1694
2416
  existente.entities = deduplicarEntidades([...existente.entities, ...modulo.entities]);
1695
2417
  existente.enums = deduplicarEnums([...existente.enums, ...modulo.enums]);
2418
+ existente.vinculos = deduplicarVinculos([...(existente.vinculos ?? []), ...(modulo.vinculos ?? [])]);
1696
2419
  }
1697
2420
  function selecionarSimbolosPreferidos(simbolos) {
1698
2421
  const mapa = new Map();
@@ -2029,6 +2752,18 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
2029
2752
  else if (fonte === "nextjs") {
2030
2753
  modulos = await importarNextJsBase(base, namespace);
2031
2754
  }
2755
+ else if (fonte === "nextjs-consumer") {
2756
+ modulos = await importarNextJsConsumerBase(base, namespace);
2757
+ }
2758
+ else if (fonte === "react-vite-consumer") {
2759
+ modulos = await importarReactViteConsumerBase(base, namespace);
2760
+ }
2761
+ else if (fonte === "angular-consumer") {
2762
+ modulos = await importarAngularConsumerBase(base, namespace);
2763
+ }
2764
+ else if (fonte === "flutter-consumer") {
2765
+ modulos = await importarFlutterConsumerBase(base, namespace);
2766
+ }
2032
2767
  else if (fonte === "firebase") {
2033
2768
  modulos = await importarFirebaseBase(base, namespace);
2034
2769
  }
@@ -2047,6 +2782,9 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
2047
2782
  else if (fonte === "dart") {
2048
2783
  modulos = await importarDartBase(base, namespace);
2049
2784
  }
2785
+ else if (fonte === "lua") {
2786
+ modulos = await importarLuaBase(base, namespace);
2787
+ }
2050
2788
  else if (fonte === "dotnet") {
2051
2789
  modulos = await importarDotnetBase(base, namespace);
2052
2790
  }