@semacode/cli 3.0.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/agentEntryPoints.js +78 -64
  2. package/dist/cliGrammar.js +4 -4
  3. package/dist/cliHelpTexto.js +6 -6
  4. package/dist/docs.part01.d.ts +4 -1
  5. package/dist/docs.part01.js +8 -0
  6. package/dist/docs.part02.js +9 -1
  7. package/dist/drift.part01.d.ts +7 -1
  8. package/dist/drift.part04.d.ts +14 -2
  9. package/dist/drift.part04.js +35 -2
  10. package/dist/drift.part09.d.ts +2 -2
  11. package/dist/drift.part09.js +10 -3
  12. package/dist/drift.part11.js +24 -3
  13. package/dist/drift.part14.js +2 -0
  14. package/dist/exemplosOficiais.d.ts +2 -1
  15. package/dist/exemplosOficiais.js +9 -3
  16. package/dist/geracaoCore.d.ts +1 -0
  17. package/dist/geracaoCore.js +2 -0
  18. package/dist/importador.part01.d.ts +1 -1
  19. package/dist/importador.part06.d.ts +2 -0
  20. package/dist/importador.part06.js +87 -1
  21. package/dist/importador.part09.js +7 -1
  22. package/dist/index.part04.js +13 -6
  23. package/dist/index.part07.d.ts +8 -2
  24. package/dist/index.part07.js +146 -4
  25. package/dist/index.part08.js +3 -3
  26. package/dist/initCommand.d.ts +1 -0
  27. package/dist/initCommand.js +15 -3
  28. package/dist/initTemplatesBase.js +78 -78
  29. package/dist/projetoConfig.js +2 -0
  30. package/dist/projetoLegado.js +18 -0
  31. package/dist/tipos.d.ts +1 -1
  32. package/dist/typescript-http-modelos.d.ts +1 -1
  33. package/dist/typescript-http-rotas.d.ts +1 -0
  34. package/dist/typescript-http-rotas.js +93 -0
  35. package/dist/typescript-http.d.ts +1 -1
  36. package/dist/typescript-http.js +1 -1
  37. package/dist/verificacaoCache.d.ts +25 -0
  38. package/dist/verificacaoCache.js +80 -0
  39. package/docs/syntax.md +14 -0
  40. package/exemplos/express_crud.sema +139 -0
  41. package/exemplos/fastify_crud.sema +146 -0
  42. package/node_modules/@sema/nucleo/dist/ir/conversor.part01.js +37 -38
  43. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +1 -0
  44. package/node_modules/@sema/nucleo/dist/semantico/analisador.part01.d.ts +7 -0
  45. package/node_modules/@sema/nucleo/dist/semantico/analisador.part01.js +33 -10
  46. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { type TemplateIniciar } from "./initTemplatesBase.js";
2
2
  export declare function comandoIniciar(cwd: string, template: TemplateIniciar, opcoes?: {
3
3
  force?: boolean;
4
+ exemplosCompletos?: boolean;
4
5
  }): Promise<number>;
5
6
  export declare function comandoInstalarExemplos(emJson: boolean): Promise<number>;
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { escreverArquivos } from "./fsGovernado.js";
6
6
  import { arquivosTemplateIniciar } from "./initTemplatesBase.js";
7
- import { materializarExemplosOficiais, planejarExemplosOficiais } from "./exemplosOficiais.js";
7
+ import { materializarExemplosOficiais, normalizarCaminhoExemplo, planejarExemplosOficiais } from "./exemplosOficiais.js";
8
8
  import { validarDestinosEscritaWorkspace } from "./workspaceWrite.js";
9
9
  async function sincronizarKitIaInicial(cwd) {
10
10
  const [{ gerarResumoProjetoIa }, { sincronizarEntrypointCodex }] = await Promise.all([
@@ -26,6 +26,12 @@ async function sincronizarKitIaInicial(cwd) {
26
26
  codex,
27
27
  };
28
28
  }
29
+ const EXEMPLOS_STARTER_INICIAR = new Set([
30
+ "exemplos/crud_simples.sema",
31
+ "exemplos/cadastro_usuario.sema",
32
+ "exemplos/pedido.sema",
33
+ "exemplos/autenticacao.sema",
34
+ ]);
29
35
  export async function comandoIniciar(cwd, template, opcoes = {}) {
30
36
  const arquivos = arquivosTemplateIniciar(template);
31
37
  const planoExemplos = await planejarExemplosOficiais();
@@ -33,6 +39,9 @@ export async function comandoIniciar(cwd, template, opcoes = {}) {
33
39
  console.error("Diretorio de exemplos oficiais nao foi encontrado no pacote da CLI.");
34
40
  return 1;
35
41
  }
42
+ const exemplosIniciar = opcoes.exemplosCompletos
43
+ ? planoExemplos.arquivos
44
+ : planoExemplos.arquivos.filter((arquivo) => EXEMPLOS_STARTER_INICIAR.has(normalizarCaminhoExemplo(arquivo.caminhoRelativo)));
36
45
  const [{ ARQUIVOS_RESUMO_PROJETO_IA }, { listarDestinosEntrypointCodex }] = await Promise.all([
37
46
  import("./index.part04.js"),
38
47
  import("./agentEntryPoints.js"),
@@ -40,7 +49,7 @@ export async function comandoIniciar(cwd, template, opcoes = {}) {
40
49
  const caminhosTemplate = new Set(arquivos.map((arquivo) => arquivo.caminhoRelativo));
41
50
  const destinos = await validarDestinosEscritaWorkspace(cwd, [
42
51
  ...caminhosTemplate,
43
- ...planoExemplos.arquivos.map((arquivo) => arquivo.caminhoRelativo),
52
+ ...exemplosIniciar.map((arquivo) => arquivo.caminhoRelativo),
44
53
  ...ARQUIVOS_RESUMO_PROJETO_IA,
45
54
  ...listarDestinosEntrypointCodex("AGENTS.md"),
46
55
  ]);
@@ -51,7 +60,7 @@ export async function comandoIniciar(cwd, template, opcoes = {}) {
51
60
  ? arquivos
52
61
  : arquivos.filter((arquivo) => !existentes.has(arquivo.caminhoRelativo));
53
62
  await escreverArquivos(cwd, arquivosParaEscrever, { inserirCabecalhoGovernado: true });
54
- const exemplos = await materializarExemplosOficiais(cwd, true);
63
+ const exemplos = await materializarExemplosOficiais(cwd, true, opcoes.exemplosCompletos ? undefined : EXEMPLOS_STARTER_INICIAR);
55
64
  if (!exemplos.sucesso) {
56
65
  console.error(exemplos.erro);
57
66
  return 1;
@@ -67,6 +76,9 @@ export async function comandoIniciar(cwd, template, opcoes = {}) {
67
76
  console.log(`Projeto Sema inicializado com template ${template}.`);
68
77
  console.log(`Arquivos do projeto: ${arquivosParaEscrever.length} escritos, ${opcoes.force ? 0 : existentes.size} preservados.`);
69
78
  console.log(`Exemplos oficiais sincronizados em ${exemplos.destino} (${exemplos.criados.length} criados, ${exemplos.preservados.length} preservados).`);
79
+ if (!opcoes.exemplosCompletos) {
80
+ console.log("Starter de exemplos instalado; rode sema instalar-exemplos ou sema iniciar --com-exemplos para o pacote completo.");
81
+ }
70
82
  console.log(`Kit IA Codex-native sincronizado (${kitIa.artefatos.length} artefatos; entrypoint ${kitIa.codex.entrypointCodex}; ${kitIa.codex.criados.length} criados, ${kitIa.codex.atualizados.length} atualizados, ${kitIa.codex.preservados.length} preservados).`);
71
83
  return 0;
72
84
  }
@@ -8,54 +8,54 @@ export function arquivosBaseIniciar() {
8
8
  return [
9
9
  {
10
10
  caminhoRelativo: "contratos/pedidos.sema",
11
- conteudo: `module app.pedidos {
12
- entity Pedido {
13
- fields {
14
- id: Id
15
- status: Texto
16
- total: Decimal
17
- }
18
- }
19
-
20
- task criar_pedido {
21
- input {
22
- cliente_id: Id required
23
- total: Decimal required
24
- }
25
- output {
26
- pedido_id: Id
27
- status: Texto
28
- }
29
- rules {
30
- total > 0
31
- }
32
- effects {
33
- persistencia Pedido criticidade=alta
34
- auditoria pedidos
35
- }
36
- guarantees {
37
- pedido_id existe
38
- status existe
39
- }
40
- tests {
41
- caso "pedido valido" {
42
- given {
43
- cliente_id: "cli-1"
44
- total: 10
45
- }
46
- expect {
47
- sucesso: verdadeiro
48
- }
49
- }
50
- }
51
- }
52
-
53
- route criar_pedido_publico {
54
- metodo: POST
55
- caminho: /pedidos
56
- task: criar_pedido
57
- }
58
- }
11
+ conteudo: `module app.pedidos {
12
+ entity Pedido {
13
+ fields {
14
+ id: Id
15
+ status: Texto
16
+ total: Decimal
17
+ }
18
+ }
19
+
20
+ task criar_pedido {
21
+ input {
22
+ cliente_id: Id required
23
+ total: Decimal required
24
+ }
25
+ output {
26
+ pedido_id: Id
27
+ status: Texto
28
+ }
29
+ rules {
30
+ total > 0
31
+ }
32
+ effects {
33
+ persistencia Pedido criticidade=alta
34
+ auditoria pedidos
35
+ }
36
+ guarantees {
37
+ pedido_id existe
38
+ status existe
39
+ }
40
+ tests {
41
+ caso "pedido valido" {
42
+ given {
43
+ cliente_id: "cli-1"
44
+ total: 10
45
+ }
46
+ expect {
47
+ sucesso: verdadeiro
48
+ }
49
+ }
50
+ }
51
+ }
52
+
53
+ route criar_pedido_publico {
54
+ metodo: POST
55
+ caminho: /pedidos
56
+ task: criar_pedido
57
+ }
58
+ }
59
59
  `,
60
60
  },
61
61
  ];
@@ -64,41 +64,41 @@ function arquivosApoioIaIniciar() {
64
64
  return [
65
65
  {
66
66
  caminhoRelativo: "README.md",
67
- conteudo: `# Projeto Sema
68
-
69
- Este projeto foi inicializado com Sema.
70
-
71
- ## Entrada oficial do Codex
72
-
73
- Antes de alterar codigo, contrato ou documentacao:
74
-
75
- 1. leia \`AGENTS.md\`;
67
+ conteudo: `# Projeto Sema
68
+
69
+ Este projeto foi inicializado com Sema.
70
+
71
+ ## Entrada oficial do Codex
72
+
73
+ Antes de alterar codigo, contrato ou documentacao:
74
+
75
+ 1. leia \`AGENTS.md\`;
76
76
  2. rode \`sema --version\`; se o shell não localizar o comando, use \`$HOME/.sema/bin/sema\` no macOS/Linux; no Windows, PowerShell usa \`sema.ps1\` no PATH, cmd.exe usa \`sema.cmd\`, e o fallback absoluto é \`& "$env:SystemRoot\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$HOME\\.sema\\bin\\sema-managed.ps1" --version\`;
77
77
  3. rode \`sema resumo --drift none\` para contexto contratual; score, confiança e implementação ficam não avaliados;
78
78
  4. rode \`sema docs-impacto --intencao "<acao>" --json\`;
79
- 5. leia \`docs/commands.md\` antes de escolher comando ou interpretar \`--saida\`;
80
- 6. use \`sema descobrir recomendar --intencao "<objetivo>" --json\` quando não souber qual capacidade ou pipeline aplicar;
79
+ 5. leia \`docs/commands.md\` antes de escolher comando ou interpretar \`--saida\`;
80
+ 6. use \`sema descobrir recomendar --intencao "<objetivo>" --json\` quando não souber qual capacidade ou pipeline aplicar;
81
81
  7. use \`exemplos/\` e \`docs/syntax.md\` antes de criar ou corrigir \`.sema\`.
82
82
 
83
83
  Quando precisar comprovar o código vivo, rode \`sema drift <contrato.sema> --escopo modulo --cache fresh --json\`. Cache acelera navegação, mas não é prova final de fechamento.
84
-
85
- ## Estrutura criada
86
-
87
- - \`contratos/\`: contratos semanticamente governados.
88
- - \`AGENTS.md\`: entrypoint oficial do Codex.
89
- - \`exemplos/\`: exemplos oficiais da DSL Sema.
90
- - \`docs/commands.md\`: catalogo de comandos, pipelines, gates e regra de \`--saida\`.
91
- - \`docs/syntax.md\`: referencia curta para escrever \`.sema\`.
92
- - \`docs/ai-workflow.md\`: fluxo minimo para agentes.
93
- - \`SEMA_BOOT.md\`, \`SEMA_SMALL_MODEL.md\`, \`AGENT_CONTEXT_PACK.json\` e \`SEMA_INDEX.json\`: contexto IA-first.
94
-
95
- ## Geracao
96
-
97
- O template base ja declara alvo JavaScript. Exemplo:
98
-
99
- \`\`\`bash
100
- sema compilar contratos/pedidos.sema --alvo javascript --saida ./generated/javascript
101
- \`\`\`
84
+
85
+ ## Estrutura criada
86
+
87
+ - \`contratos/\`: contratos semanticamente governados.
88
+ - \`AGENTS.md\`: entrypoint oficial do Codex.
89
+ - \`exemplos/\`: exemplos oficiais da DSL Sema.
90
+ - \`docs/commands.md\`: catalogo de comandos, pipelines, gates e regra de \`--saida\`.
91
+ - \`docs/syntax.md\`: referencia curta para escrever \`.sema\`.
92
+ - \`docs/ai-workflow.md\`: fluxo minimo para agentes.
93
+ - \`SEMA_BOOT.md\`, \`SEMA_SMALL_MODEL.md\`, \`AGENT_CONTEXT_PACK.json\` e \`SEMA_INDEX.json\`: contexto IA-first.
94
+
95
+ ## Geracao
96
+
97
+ O template base ja declara alvo JavaScript. Exemplo:
98
+
99
+ \`\`\`bash
100
+ sema compilar contratos/pedidos.sema --alvo javascript --saida ./generated/javascript
101
+ \`\`\`
102
102
  `,
103
103
  },
104
104
  ];
@@ -119,7 +119,7 @@ export function arquivosTemplateIniciar(template) {
119
119
  ?? [
120
120
  {
121
121
  caminhoRelativo: "sema.config.json",
122
- conteudo: "{\n \"origens\": [\"./contratos\"],\n \"saida\": \"./generated\",\n \"alvos\": [\"typescript\", \"javascript\", \"python\", \"php\", \"dart\", \"lua\", \"html\", \"css\"],\n \"alvoPadrao\": \"typescript\",\n \"estruturaSaida\": \"modulos\",\n \"framework\": \"base\",\n \"modoEstrito\": true,\n \"diretoriosSaidaPorAlvo\": {\n \"typescript\": \"./generated/typescript\",\n \"javascript\": \"./generated/javascript\",\n \"python\": \"./generated/python\",\n \"php\": \"./generated/php\",\n \"dart\": \"./generated/dart\",\n \"lua\": \"./generated/lua\",\n \"html\": \"./generated/html\",\n \"css\": \"./generated/css\",\n \"dotnet\": \"./generated/dotnet\",\n \"cpp\": \"./generated/cpp\"\n },\n \"convencoesGeracaoPorProjeto\": \"base\"\n}\n",
122
+ conteudo: "{\n \"origens\": [\"./contratos\"],\n \"saida\": \"./generated\",\n \"alvos\": [\"typescript\", \"javascript\"],\n \"alvoPadrao\": \"typescript\",\n \"estruturaSaida\": \"modulos\",\n \"framework\": \"base\",\n \"modoEstrito\": true,\n \"diretoriosSaidaPorAlvo\": {\n \"typescript\": \"./generated/typescript\",\n \"javascript\": \"./generated/javascript\",\n \"python\": \"./generated/python\",\n \"php\": \"./generated/php\",\n \"dart\": \"./generated/dart\",\n \"lua\": \"./generated/lua\",\n \"html\": \"./generated/html\",\n \"css\": \"./generated/css\",\n \"dotnet\": \"./generated/dotnet\",\n \"cpp\": \"./generated/cpp\"\n },\n \"convencoesGeracaoPorProjeto\": \"base\"\n}\n",
123
123
  },
124
124
  ...arquivosBase,
125
125
  ];
@@ -74,6 +74,8 @@ export function normalizarFonteLegado(valor) {
74
74
  return "typescript";
75
75
  }
76
76
  if (valor === "nestjs"
77
+ || valor === "express"
78
+ || valor === "fastify"
77
79
  || valor === "fastapi"
78
80
  || valor === "flask"
79
81
  || valor === "nextjs"
@@ -17,6 +17,14 @@ export async function inferirFontesLegado(diretoriosCodigo, baseProjeto, configC
17
17
  if (/@nestjs\/common|@nestjs\/core/.test(packageJsonRaiz)) {
18
18
  encontrados.add("nestjs");
19
19
  }
20
+ if (/"express"\s*:/.test(packageJsonRaiz)) {
21
+ encontrados.add("express");
22
+ encontrados.add("typescript");
23
+ }
24
+ if (/"fastify"\s*:/.test(packageJsonRaiz)) {
25
+ encontrados.add("fastify");
26
+ encontrados.add("typescript");
27
+ }
20
28
  if (/typescript/.test(packageJsonRaiz)) {
21
29
  encontrados.add("typescript");
22
30
  }
@@ -64,6 +72,10 @@ export async function inferirFontesLegado(diretoriosCodigo, baseProjeto, configC
64
72
  const relacoesTs = arquivosTsJs.map((arquivo) => path.relative(diretorio, arquivo).replace(/\\/g, "/"));
65
73
  const temNest = textosPackage.some((texto) => /@nestjs\/common|@nestjs\/core/.test(texto ?? ""))
66
74
  || amostrasTs.some((texto) => /@nestjs\/common|@nestjs\/core|@Controller\(|@Get\(|@Post\(|@Put\(|@Patch\(|@Delete\(/.test(texto ?? ""));
75
+ const temExpress = textosPackage.some((texto) => /"express"\s*:/.test(texto ?? ""))
76
+ || amostrasTs.some((texto) => /(?:from\s+["']express["']|require\(\s*["']express["']\s*\)|\bexpress\s*\(\s*\))/.test(texto ?? ""));
77
+ const temFastify = textosPackage.some((texto) => /"fastify"\s*:/.test(texto ?? ""))
78
+ || amostrasTs.some((texto) => /(?:from\s+["']fastify["']|require\(\s*["']fastify["']\s*\)|\bfastify\s*\(\s*\{)/.test(texto ?? ""));
67
79
  const temNext = textosPackage.some((texto) => /"next"\s*:/.test(texto ?? ""))
68
80
  || nextConfigs.length > 0
69
81
  || relacoesTs.some((relacao) => /(?:^|\/)(?:src\/)?app\/api\/.+\/route\.(?:ts|tsx|js|jsx)$/.test(relacao));
@@ -86,6 +98,12 @@ export async function inferirFontesLegado(diretoriosCodigo, baseProjeto, configC
86
98
  if (temNest) {
87
99
  encontrados.add("nestjs");
88
100
  }
101
+ if (temExpress) {
102
+ encontrados.add("express");
103
+ }
104
+ if (temFastify) {
105
+ encontrados.add("fastify");
106
+ }
89
107
  if (temNext) {
90
108
  encontrados.add("nextjs");
91
109
  }
package/dist/tipos.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export type EstruturaSaida = "flat" | "modulos" | "backend";
2
- export type FonteLegado = "nestjs" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "javascript" | "python" | "dart" | "lua" | "dotnet" | "java" | "go" | "rust" | "cpp" | "php";
2
+ export type FonteLegado = "nestjs" | "express" | "fastify" | "fastapi" | "flask" | "nextjs" | "nextjs-consumer" | "react-vite-consumer" | "angular-consumer" | "flutter-consumer" | "firebase" | "typescript" | "javascript" | "python" | "dart" | "lua" | "dotnet" | "java" | "go" | "rust" | "cpp" | "php";
3
3
  export type ModoAdocao = "incremental";
@@ -4,7 +4,7 @@ export interface ParametroRotaTypeScript {
4
4
  tipoSema: "Texto" | "Inteiro" | "Decimal" | "Id";
5
5
  }
6
6
  export interface RotaTypeScriptExtraida {
7
- origem: "nextjs" | "firebase";
7
+ origem: "nextjs" | "firebase" | "express" | "fastify";
8
8
  metodo: string;
9
9
  caminho: string;
10
10
  simbolo: string;
@@ -6,4 +6,5 @@ export declare function inferirCaminhoNext(relacaoArquivo: string): {
6
6
  } | undefined;
7
7
  export declare function identificarRotasNodeHttp(sourceFile: ts.SourceFile, nomeSimbolo: string, corpo: ts.Block): RotaTypeScriptExtraida[];
8
8
  export declare function extrairRotasNodeWorker(sourceFile: ts.SourceFile, relacaoArquivo: string): RotaTypeScriptExtraida[];
9
+ export declare function extrairRotasExpressFastify(sourceFile: ts.SourceFile): RotaTypeScriptExtraida[];
9
10
  export declare function extrairRotasTypeScriptHttp(sourceFile: ts.SourceFile, relacaoArquivo: string): RotaTypeScriptExtraida[];
@@ -109,6 +109,98 @@ export function extrairRotasNodeWorker(sourceFile, relacaoArquivo) {
109
109
  }
110
110
  return deduplicarRotas(rotas);
111
111
  }
112
+ export function extrairRotasExpressFastify(sourceFile) {
113
+ const textoArquivo = sourceFile.getFullText();
114
+ const importouExpress = /(?:from\s+["']express["']|require\(\s*["']express["']\s*\))/.test(textoArquivo);
115
+ const importouFastify = /(?:from\s+["']fastify["']|require\(\s*["']fastify["']\s*\))/.test(textoArquivo);
116
+ if (!importouExpress && !importouFastify) {
117
+ return [];
118
+ }
119
+ const rotas = [];
120
+ const escolherOrigemChamada = (receptor) => {
121
+ if (/fastify/i.test(receptor)) {
122
+ return "fastify";
123
+ }
124
+ if (importouExpress) {
125
+ return "express";
126
+ }
127
+ if (importouFastify) {
128
+ return "fastify";
129
+ }
130
+ return undefined;
131
+ };
132
+ const visitar = (node) => {
133
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.expression)) {
134
+ const receptor = node.expression.expression.text;
135
+ const nomeMetodo = node.expression.name.text.toLowerCase();
136
+ if (nomeMetodo === "route" && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) && importouFastify) {
137
+ let metodo;
138
+ let caminho;
139
+ for (const propriedade of node.arguments[0].properties) {
140
+ if (!ts.isPropertyAssignment(propriedade) || !ts.isIdentifier(propriedade.name)) {
141
+ continue;
142
+ }
143
+ const valor = propriedade.initializer && ts.isStringLiteralLike(propriedade.initializer)
144
+ ? propriedade.initializer.text
145
+ : undefined;
146
+ if (propriedade.name.text === "method" && valor) {
147
+ metodo = valor.toUpperCase();
148
+ }
149
+ if (propriedade.name.text === "url" && valor) {
150
+ caminho = valor;
151
+ }
152
+ }
153
+ if (metodo && caminho?.startsWith("/") && METODOS_HTTP.has(metodo)) {
154
+ rotas.push({
155
+ origem: "fastify",
156
+ metodo,
157
+ caminho: converterCaminhoParametros(caminho),
158
+ simbolo: receptor,
159
+ parametros: extrairParametrosCaminhoDoisPontos(caminho),
160
+ });
161
+ }
162
+ }
163
+ else if (nomeMetodo === "all" || METODOS_HTTP.has(nomeMetodo.toUpperCase())) {
164
+ const caminho = node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])
165
+ ? node.arguments[0].text
166
+ : undefined;
167
+ const origem = escolherOrigemChamada(receptor);
168
+ const handler = node.arguments[1] && ts.isIdentifier(node.arguments[1]) ? node.arguments[1].text : undefined;
169
+ if (caminho?.startsWith("/") && origem) {
170
+ const metodos = nomeMetodo === "all"
171
+ ? ["GET", "POST", "PUT", "PATCH", "DELETE"]
172
+ : [nomeMetodo.toUpperCase()];
173
+ for (const metodo of metodos) {
174
+ rotas.push({
175
+ origem,
176
+ metodo,
177
+ caminho: converterCaminhoParametros(caminho),
178
+ simbolo: handler ?? receptor,
179
+ parametros: extrairParametrosCaminhoDoisPontos(caminho),
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+ node.forEachChild(visitar);
186
+ };
187
+ visitar(sourceFile);
188
+ return deduplicarRotas(rotas);
189
+ }
190
+ function converterCaminhoParametros(caminho) {
191
+ return caminho.replace(/:([A-Za-z_]\w*)/g, "{$1}");
192
+ }
193
+ function extrairParametrosCaminhoDoisPontos(caminho) {
194
+ const parametros = [];
195
+ for (const correspondencia of caminho.matchAll(/:([A-Za-z_]\w*)/g)) {
196
+ const nome = correspondencia[1];
197
+ parametros.push({
198
+ nome,
199
+ tipoSema: /(^|_)id$/i.test(nome) ? "Id" : "Texto",
200
+ });
201
+ }
202
+ return parametros;
203
+ }
112
204
  export function extrairRotasTypeScriptHttp(sourceFile, relacaoArquivo) {
113
205
  const rotas = [];
114
206
  const caminhoNext = inferirCaminhoNext(relacaoArquivo);
@@ -124,6 +216,7 @@ export function extrairRotasTypeScriptHttp(sourceFile, relacaoArquivo) {
124
216
  }
125
217
  }
126
218
  rotas.push(...extrairRotasNodeWorker(sourceFile, relacaoArquivo));
219
+ rotas.push(...extrairRotasExpressFastify(sourceFile));
127
220
  return deduplicarRotas(rotas);
128
221
  }
129
222
  //# sourceMappingURL=typescript-http-rotas.js.map
@@ -1,3 +1,3 @@
1
1
  export type { CampoInferidoTypeScriptHttp, ExportacaoTypeScriptHttp, ParametroRotaTypeScript, RotaTypeScriptExtraida, SemanticaHandlerTypeScriptHttp, } from "./typescript-http-modelos.js";
2
- export { extrairRotasTypeScriptHttp } from "./typescript-http-rotas.js";
2
+ export { extrairRotasExpressFastify, extrairRotasTypeScriptHttp } from "./typescript-http-rotas.js";
3
3
  export { inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http-semantica.js";
@@ -1,5 +1,5 @@
1
1
  // SEMA-GOVERNED: sema.governanca_ia_contexto
2
2
  // Descricao: fachada de extracao HTTP TypeScript; consulte contratos/sema/governanca_ia_contexto.sema antes de editar.
3
- export { extrairRotasTypeScriptHttp } from "./typescript-http-rotas.js";
3
+ export { extrairRotasExpressFastify, extrairRotasTypeScriptHttp } from "./typescript-http-rotas.js";
4
4
  export { inferirSemanticaHandlerTypeScriptHttp, localizarExportacaoTypeScriptHttp, } from "./typescript-http-semantica.js";
5
5
  //# sourceMappingURL=typescript-http.js.map
@@ -0,0 +1,25 @@
1
+ export declare const SCHEMA_CACHE_VERIFICACAO = "sema.verificacao-cache/v1";
2
+ export interface EntradaCacheVerificacao {
3
+ schemaVersion: typeof SCHEMA_CACHE_VERIFICACAO;
4
+ chave: string;
5
+ versaoCli: string;
6
+ alvo: string;
7
+ modulo: string;
8
+ sucesso: boolean;
9
+ quantidadeTestes: number;
10
+ testesExecutados: boolean;
11
+ arquivosGerados: string[];
12
+ geradoEm: string;
13
+ }
14
+ export declare function calcularChaveVerificacao(entradas: {
15
+ versaoCli: string;
16
+ versaoNode: string;
17
+ modulo: string;
18
+ alvo: string;
19
+ framework: string;
20
+ estrutura: string;
21
+ conteudoContrato: string;
22
+ }): string;
23
+ export declare function carregarCacheVerificacao(chave: string, pastaAlvo: string, raizCache?: string): Promise<EntradaCacheVerificacao | undefined>;
24
+ export declare function carregarManifestoVerificacao(chave: string, raizCache?: string): Promise<EntradaCacheVerificacao | undefined>;
25
+ export declare function gravarCacheVerificacao(entrada: EntradaCacheVerificacao, raizCache?: string): Promise<void>;
@@ -0,0 +1,80 @@
1
+ // SEMA-GOVERNED: sema.produto.cli_verificacao
2
+ // Descrição: cache incremental de verificação fora do workspace, endereçado por conteúdo do contrato.
3
+ import { createHash } from "node:crypto";
4
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { resolverRaizCacheSema } from "./driftCacheFilesystem.js";
7
+ export const SCHEMA_CACHE_VERIFICACAO = "sema.verificacao-cache/v1";
8
+ export function calcularChaveVerificacao(entradas) {
9
+ return createHash("sha256")
10
+ .update(JSON.stringify({
11
+ schemaVersion: SCHEMA_CACHE_VERIFICACAO,
12
+ versaoCli: entradas.versaoCli,
13
+ versaoNode: entradas.versaoNode,
14
+ modulo: entradas.modulo,
15
+ alvo: entradas.alvo,
16
+ framework: entradas.framework,
17
+ estrutura: entradas.estrutura,
18
+ conteudoContrato: entradas.conteudoContrato,
19
+ }))
20
+ .digest("hex");
21
+ }
22
+ function resolverPastaCacheVerificacao(raizCache) {
23
+ const resolucao = resolverRaizCacheSema(raizCache ? { raizCache } : {});
24
+ return resolucao.disponivel ? path.join(resolucao.raiz, "verificacao") : undefined;
25
+ }
26
+ export async function carregarCacheVerificacao(chave, pastaAlvo, raizCache) {
27
+ const pasta = resolverPastaCacheVerificacao(raizCache);
28
+ if (!pasta) {
29
+ return undefined;
30
+ }
31
+ let entrada;
32
+ try {
33
+ entrada = JSON.parse(await readFile(path.join(pasta, `${chave}.json`), "utf8"));
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ if (entrada.schemaVersion !== SCHEMA_CACHE_VERIFICACAO || entrada.chave !== chave) {
39
+ return undefined;
40
+ }
41
+ for (const relativo of entrada.arquivosGerados) {
42
+ try {
43
+ await stat(path.join(pastaAlvo, relativo));
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ return entrada;
50
+ }
51
+ export async function carregarManifestoVerificacao(chave, raizCache) {
52
+ const pasta = resolverPastaCacheVerificacao(raizCache);
53
+ if (!pasta) {
54
+ return undefined;
55
+ }
56
+ try {
57
+ const entrada = JSON.parse(await readFile(path.join(pasta, `${chave}.json`), "utf8"));
58
+ if (entrada.schemaVersion !== SCHEMA_CACHE_VERIFICACAO || entrada.chave !== chave) {
59
+ return undefined;
60
+ }
61
+ return entrada;
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
67
+ export async function gravarCacheVerificacao(entrada, raizCache) {
68
+ const pasta = resolverPastaCacheVerificacao(raizCache);
69
+ if (!pasta) {
70
+ return;
71
+ }
72
+ try {
73
+ await mkdir(pasta, { recursive: true });
74
+ await writeFile(path.join(pasta, `${entrada.chave}.json`), `${JSON.stringify(entrada, null, 2)}\n`, "utf8");
75
+ }
76
+ catch {
77
+ // Cache é aceleração: falha de gravação nunca derruba a verificação.
78
+ }
79
+ }
80
+ //# sourceMappingURL=verificacaoCache.js.map
package/docs/syntax.md CHANGED
@@ -87,6 +87,20 @@ impl {
87
87
 
88
88
  `sema compilar --alvo javascript` defines a generation target. `impl { js: ... }` defines the live-code origin linked to the contract. They are different layers and both are valid.
89
89
 
90
+ ## Layered `impl` Roles
91
+
92
+ Layered code (route + service + persistence in the same language) can declare one `impl` per role. Append a supported role to the origin: `rota`, `servico`, `persistencia` or `repositorio`.
93
+
94
+ ```sema
95
+ impl {
96
+ ts_rota: server.routes.monitores.criarMonitor
97
+ ts_servico: server.services.monitores.criarMonitor
98
+ ts_persistencia: server.repositories.monitores.criar
99
+ }
100
+ ```
101
+
102
+ Each origin-plus-role pair must appear at most once; the bare origin (for example `ts`) stays valid for single-layer tasks, and drift resolves every role independently.
103
+
90
104
  ## Small Canonical Lists
91
105
 
92
106
  - `effects`: `persistencia`, `consulta`, `evento`, `auditoria`, `db.write`, `queue.publish`, `fs.write`, `network.egress`, `secret.read`, `shell.exec`.