@semacode/cli 1.5.10 → 1.5.14

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 (44) hide show
  1. package/README.md +6 -1
  2. package/SEMA_BRIEF.curto.txt +3 -3
  3. package/SEMA_BRIEF.md +5 -5
  4. package/SEMA_BRIEF.micro.txt +2 -2
  5. package/SEMA_INDEX.json +50 -32
  6. package/dist/drift.d.ts +3 -3
  7. package/dist/drift.js +67 -4
  8. package/dist/drift.js.map +1 -1
  9. package/dist/importador.d.ts +1 -1
  10. package/dist/importador.js +144 -1
  11. package/dist/importador.js.map +1 -1
  12. package/dist/index.js +388 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/php-symbols.d.ts +24 -0
  15. package/dist/php-symbols.js +375 -0
  16. package/dist/php-symbols.js.map +1 -0
  17. package/dist/projeto.js +14 -0
  18. package/dist/projeto.js.map +1 -1
  19. package/dist/tipos.d.ts +1 -1
  20. package/docs/cli.md +10 -1
  21. package/docs/sintaxe.md +192 -0
  22. package/node_modules/@sema/gerador-css/package.json +1 -1
  23. package/node_modules/@sema/gerador-dart/package.json +1 -1
  24. package/node_modules/@sema/gerador-html/package.json +1 -1
  25. package/node_modules/@sema/gerador-javascript/package.json +1 -1
  26. package/node_modules/@sema/gerador-lua/package.json +1 -1
  27. package/node_modules/@sema/gerador-python/package.json +1 -1
  28. package/node_modules/@sema/gerador-typescript/package.json +1 -1
  29. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +4 -2
  30. package/node_modules/@sema/nucleo/dist/formatador/index.js +40 -13
  31. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -1
  32. package/node_modules/@sema/nucleo/dist/ir/conversor.js +182 -3
  33. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
  34. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +35 -3
  35. package/node_modules/@sema/nucleo/dist/lexer/tokens.js +21 -0
  36. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -1
  37. package/node_modules/@sema/nucleo/dist/parser/parser.js +38 -0
  38. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
  39. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +4 -3
  40. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +310 -13
  41. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
  42. package/node_modules/@sema/nucleo/package.json +1 -1
  43. package/node_modules/@sema/padroes/package.json +1 -1
  44. package/package.json +14 -14
@@ -1,5 +1,5 @@
1
1
  import { type Diagnostico } from "@sema/nucleo";
2
- export type FonteImportacao = "nestjs" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "python" | "dart" | "lua" | "dotnet" | "java" | "go" | "rust" | "cpp";
2
+ export type FonteImportacao = "nestjs" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "python" | "dart" | "lua" | "php" | "dotnet" | "java" | "go" | "rust" | "cpp";
3
3
  export interface ArquivoImportado {
4
4
  caminhoRelativo: string;
5
5
  conteudo: string;
@@ -8,6 +8,7 @@ import { extrairRotasDotnet, extrairSimbolosDotnet } from "./dotnet-http.js";
8
8
  import { extrairRotasGo, extrairSimbolosGo } from "./go-http.js";
9
9
  import { extrairRotasJava, extrairSimbolosJava } from "./java-http.js";
10
10
  import { extrairSimbolosLua } from "./lua-symbols.js";
11
+ import { extrairRotasPhp, extrairSimbolosPhp } from "./php-symbols.js";
11
12
  import { extrairParametrosCaminhoFlask, extrairRotasFlaskDecoradas } from "./python-http.js";
12
13
  import { extrairRotasRust, extrairSimbolosRust } from "./rust-http.js";
13
14
  import { extrairRotasTypeScriptHttp, inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http.js";
@@ -246,6 +247,7 @@ function limparTipoBackend(tipo) {
246
247
  }
247
248
  return tipo
248
249
  .trim()
250
+ .replace(/^\?/, "")
249
251
  .replace(/^Task<(.+)>$/i, "$1")
250
252
  .replace(/^ActionResult<(.+)>$/i, "$1")
251
253
  .replace(/^IActionResult$/i, "Json")
@@ -259,7 +261,10 @@ function limparTipoBackend(tipo) {
259
261
  .replace(/^Vec<(.+)>$/i, "Json")
260
262
  .replace(/^List<(.+)>$/i, "Json")
261
263
  .replace(/^Map<(.+)>$/i, "Json")
262
- .replace(/^Dictionary<(.+)>$/i, "Json");
264
+ .replace(/^Dictionary<(.+)>$/i, "Json")
265
+ .replace(/\|null$/i, "")
266
+ .replace(/^null\|/i, "")
267
+ .replace(/^(array|mixed|object)$/i, "Json");
263
268
  }
264
269
  function mapearTipoBackendParaSema(tipo) {
265
270
  const limpo = limparTipoBackend(tipo);
@@ -1466,6 +1471,7 @@ function renderizarImpl(impl, indentacao = " ") {
1466
1471
  ...(impl.py ? [`${indentacao} py: ${impl.py}`] : []),
1467
1472
  ...(impl.dart ? [`${indentacao} dart: ${impl.dart}`] : []),
1468
1473
  ...(impl.lua ? [`${indentacao} lua: ${impl.lua}`] : []),
1474
+ ...(impl.php ? [`${indentacao} php: ${impl.php}`] : []),
1469
1475
  ...(impl.cs ? [`${indentacao} cs: ${impl.cs}`] : []),
1470
1476
  ...(impl.java ? [`${indentacao} java: ${impl.java}`] : []),
1471
1477
  ...(impl.go ? [`${indentacao} go: ${impl.go}`] : []),
@@ -2193,6 +2199,19 @@ function extrairErrosLua(texto) {
2193
2199
  }
2194
2200
  return [...erros.entries()].map(([nome, mensagem]) => ({ nome, mensagem }));
2195
2201
  }
2202
+ function extrairErrosPhp(texto) {
2203
+ const erros = new Map();
2204
+ for (const match of texto.matchAll(/\bthrow\s+new\s+([A-Za-z_\\][A-Za-z0-9_\\]*)\s*\(([^)]*)\)/g)) {
2205
+ const nomeBruto = match[1].split("\\").at(-1) ?? match[1];
2206
+ const mensagem = (match[2] ?? "").match(/["']([^"']+)["']/)?.[1] ?? `Erro importado automaticamente de ${nomeBruto}.`;
2207
+ erros.set(normalizarNomeErroBruto(nomeBruto), mensagem);
2208
+ }
2209
+ for (const match of texto.matchAll(/\babort\s*\(\s*(\d{3})(?:\s*,\s*["']([^"']+)["'])?/g)) {
2210
+ const mensagem = match[2] ?? `Abort HTTP ${match[1]}.`;
2211
+ erros.set(`http_${match[1]}`, mensagem);
2212
+ }
2213
+ return [...erros.entries()].map(([nome, mensagem]) => ({ nome, mensagem }));
2214
+ }
2196
2215
  function caminhoImplGenerico(diretorioBase, arquivo, simbolo, opcoes) {
2197
2216
  const relativo = path.relative(diretorioBase, arquivo).replace(/\.[^.]+$/, "");
2198
2217
  const segmentos = relativo.split(path.sep).map((segmento, indice, lista) => opcoes?.snakeCaseUltimoArquivo && indice === lista.length - 1
@@ -2207,6 +2226,62 @@ function caminhoImplPython(diretorioBase, arquivo, simbolo) {
2207
2226
  function caminhoImplDart(diretorioBase, arquivo, simbolo) {
2208
2227
  return caminhoImplGenerico(diretorioBase, arquivo, simbolo);
2209
2228
  }
2229
+ function caminhoImplPhp(diretorioBase, arquivo, simbolo) {
2230
+ return simbolo.includes(".")
2231
+ ? simbolo
2232
+ : caminhoImplGenerico(diretorioBase, arquivo, simbolo);
2233
+ }
2234
+ const NOMES_RESERVADOS_TASK_IMPORTADA = new Set([
2235
+ "database",
2236
+ "table",
2237
+ "view",
2238
+ "query",
2239
+ "transaction",
2240
+ "index",
2241
+ "constraint",
2242
+ "relationship",
2243
+ "collection",
2244
+ "document",
2245
+ "keyspace",
2246
+ "stream",
2247
+ "lock",
2248
+ "retention",
2249
+ "replication",
2250
+ "input",
2251
+ "output",
2252
+ "rules",
2253
+ "effects",
2254
+ "impl",
2255
+ "vinculos",
2256
+ "execucao",
2257
+ "auth",
2258
+ "authz",
2259
+ "dados",
2260
+ "audit",
2261
+ "segredos",
2262
+ "forbidden",
2263
+ "guarantees",
2264
+ "state",
2265
+ "tests",
2266
+ "error",
2267
+ "flow",
2268
+ "route",
2269
+ "worker",
2270
+ "evento",
2271
+ "fila",
2272
+ "cron",
2273
+ "webhook",
2274
+ "cache",
2275
+ "storage",
2276
+ "policy",
2277
+ "when",
2278
+ "given",
2279
+ "expect",
2280
+ ]);
2281
+ function normalizarNomeTaskImportada(valor) {
2282
+ const nome = paraSnakeCase(valor) || "task_importada";
2283
+ return NOMES_RESERVADOS_TASK_IMPORTADA.has(nome) ? `${nome}_task` : nome;
2284
+ }
2210
2285
  function dividirParametrosPython(parametros) {
2211
2286
  const partes = [];
2212
2287
  let atual = "";
@@ -2553,6 +2628,71 @@ async function importarLuaBase(diretorio, namespaceBase) {
2553
2628
  }
2554
2629
  return modulos;
2555
2630
  }
2631
+ async function importarPhpBase(diretorio, namespaceBase) {
2632
+ const arquivos = (await listarArquivosRecursivos(diretorio, [".php"]))
2633
+ .filter((arquivo) => !/(^|[\\/])tests?([\\/]|$)|(?:^|[\\/])spec([\\/]|$)|(?:Test|Spec)\.php$/i.test(arquivo));
2634
+ const modulos = new Map();
2635
+ for (const arquivo of arquivos) {
2636
+ const texto = await readFile(arquivo, "utf8");
2637
+ const relacao = path.relative(diretorio, arquivo);
2638
+ const contextoSegmentos = inferirContextoPorArquivo(relacao);
2639
+ const nomeModulo = [namespaceBase, ...contextoSegmentos].join(".");
2640
+ const tasks = [];
2641
+ const routes = [];
2642
+ for (const simbolo of selecionarSimbolosPreferidos(extrairSimbolosPhp(texto))) {
2643
+ const nomeBase = simbolo.simbolo.split(".").at(-1) ?? simbolo.simbolo;
2644
+ tasks.push({
2645
+ nome: normalizarNomeTaskImportada(nomeBase),
2646
+ resumo: `Task importada automaticamente de ${relacao}#${simbolo.simbolo}.`,
2647
+ input: simbolo.parametros.map((parametro) => ({
2648
+ nome: normalizarNomeCampoImportado(parametro.nome),
2649
+ tipo: mapearTipoBackendParaSema(parametro.tipoTexto),
2650
+ obrigatorio: parametro.obrigatorio,
2651
+ })),
2652
+ output: criarCampoResultadoBackend(simbolo.retorno),
2653
+ errors: extrairErrosPhp(texto),
2654
+ effects: descreverEfeitosPorHeuristica(texto),
2655
+ impl: { php: caminhoImplPhp(diretorio, arquivo, simbolo.simbolo) },
2656
+ origemArquivo: relacao,
2657
+ origemSimbolo: simbolo.simbolo,
2658
+ });
2659
+ }
2660
+ for (const rota of extrairRotasPhp(texto)) {
2661
+ const taskNome = normalizarNomeTaskImportada(rota.simbolo.split(".").at(-1) ?? rota.simbolo);
2662
+ const output = rota.retorno ? criarCampoResultadoBackend(rota.retorno) : [{ nome: "resultado", tipo: "Json", obrigatorio: false }];
2663
+ const nomeBase = `${taskNome}_publico`;
2664
+ const nome = routes.some((route) => route.nome === nomeBase)
2665
+ ? `${taskNome}_${rota.metodo.toLowerCase()}_publico`
2666
+ : nomeBase;
2667
+ tasks.push({
2668
+ nome: taskNome,
2669
+ resumo: `Task HTTP PHP importada automaticamente de ${relacao}#${rota.simbolo}.`,
2670
+ input: camposDeParametrosRotaBackend(rota.parametros),
2671
+ output,
2672
+ errors: extrairErrosPhp(texto),
2673
+ effects: [{ categoria: "consulta", alvo: "http", criticidade: "media" }],
2674
+ impl: { php: caminhoImplPhp(diretorio, arquivo, rota.simbolo) },
2675
+ origemArquivo: relacao,
2676
+ origemSimbolo: rota.simbolo,
2677
+ });
2678
+ routes.push({
2679
+ nome,
2680
+ resumo: `Rota PHP importada automaticamente de ${relacao}#${rota.simbolo}.`,
2681
+ metodo: rota.metodo,
2682
+ caminho: rota.caminho,
2683
+ task: taskNome,
2684
+ input: camposDeParametrosRotaBackend(rota.parametros),
2685
+ output,
2686
+ errors: [],
2687
+ });
2688
+ }
2689
+ if (tasks.length === 0 && routes.length === 0) {
2690
+ continue;
2691
+ }
2692
+ acumularModuloImportado(modulos, criarModuloImportadoSimples(nomeModulo, `Rascunho Sema importado automaticamente de ${relacao}.`, tasks, routes, [], inferirDatabasesPorHeuristica(texto, relacao)));
2693
+ }
2694
+ return [...modulos.values()];
2695
+ }
2556
2696
  function criarModuloImportadoSimples(nome, resumo, tasks, routes = [], vinculos = [], databases = []) {
2557
2697
  sincronizarRotasComTasks(routes, tasks);
2558
2698
  return {
@@ -2947,6 +3087,9 @@ export async function importarProjetoLegado(fonte, diretorio, namespaceBase) {
2947
3087
  else if (fonte === "lua") {
2948
3088
  modulos = await importarLuaBase(base, namespace);
2949
3089
  }
3090
+ else if (fonte === "php") {
3091
+ modulos = await importarPhpBase(base, namespace);
3092
+ }
2950
3093
  else if (fonte === "dotnet") {
2951
3094
  modulos = await importarDotnetBase(base, namespace);
2952
3095
  }