@semacode/cli 4.1.1 → 4.1.3
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.
- package/dist/cliGrammar.js +1 -1
- package/dist/cliHelpTexto.js +3 -2
- package/dist/geracaoCore.d.ts +1 -1
- package/dist/index.part07.d.ts +1 -0
- package/dist/index.part07.js +54 -31
- package/dist/index.part08.js +2 -2
- package/dist/verificacaoPythonLocal.d.ts +17 -0
- package/dist/verificacaoPythonLocal.js +46 -0
- package/docs/testing.md +18 -0
- package/node_modules/@sema/gerador-typescript/dist/index.part01.d.ts +2 -2
- package/node_modules/@sema/gerador-typescript/dist/index.part01.js +118 -16
- package/node_modules/@sema/gerador-typescript/dist/index.part02.d.ts +1 -1
- package/node_modules/@sema/gerador-typescript/dist/index.part02.js +10 -3
- package/package.json +1 -1
package/dist/cliGrammar.js
CHANGED
|
@@ -461,7 +461,7 @@ function validarComandoBasico(comando, args) {
|
|
|
461
461
|
const estrutura = valorOpcao(parsed, "--estrutura"), framework = valorOpcao(parsed, "--framework");
|
|
462
462
|
return Boolean(alvo && ALVOS_GERACAO.has(alvo)) && (!estrutura || ESTRUTURAS.has(estrutura)) && (!framework || FRAMEWORKS.has(framework));
|
|
463
463
|
}
|
|
464
|
-
case "verificar": return parsearArgumentos(args, { opcoes: ["--saida", "--alvo"], flags: ["--sem-cache"], maxPosicionais: 1 }) !== null;
|
|
464
|
+
case "verificar": return parsearArgumentos(args, { opcoes: ["--saida", "--alvo"], flags: ["--sem-cache", "--preservar-implementacao"], maxPosicionais: 1 }) !== null;
|
|
465
465
|
case "sessao": return parsearArgumentos(args, { flags: ["--json"], maxPosicionais: 2 }) !== null;
|
|
466
466
|
case "formatar": return parsearArgumentos(args, { flags: ["--check"], maxPosicionais: 1 }) !== null;
|
|
467
467
|
case "inspecionar": {
|
package/dist/cliHelpTexto.js
CHANGED
|
@@ -29,7 +29,8 @@ export function criarAjudaRaiz(versao) {
|
|
|
29
29
|
"Arquivos existentes são preservados; use --force somente para sobrescrita explícita. O starter de exemplos vem por padrão; --com-exemplos instala o pacote completo. Em subpasta de workspace já governado, a subpasta é registrada nos diretoriosCodigo do sema.config.json ancestral em vez de criar config local.",
|
|
30
30
|
"sema validar contratos/<modulo>.sema --json",
|
|
31
31
|
"sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio>",
|
|
32
|
-
"sema verificar <arquivo-ou-pasta> [--saida <dir>] [--alvo <alvo>] [--sem-cache] --json",
|
|
32
|
+
"sema verificar <arquivo-ou-pasta> [--saida <dir>] [--alvo <alvo>] [--sem-cache] [--preservar-implementacao] --json",
|
|
33
|
+
" --preservar-implementacao: somente Python base; preserva implementação local, regenera testes e ignora cache. Não comprova produção.",
|
|
33
34
|
"",
|
|
34
35
|
"[2] Editar projeto que já usa Sema",
|
|
35
36
|
"sema inspecionar . [--drift <none|cache|fresh>] --json",
|
|
@@ -121,7 +122,7 @@ export function criarAjudaRaiz(versao) {
|
|
|
121
122
|
"geração: sema compilar <arquivo-ou-pasta> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
|
|
122
123
|
"teste local: sema testar <arquivo.sema> --alvo <typescript|python|php|dart|lua|javascript|html|css|dotnet|cpp> --saida <diretorio-temporario> [--estrutura <flat|modulos|backend>] [--framework <base|nestjs|fastapi>]",
|
|
123
124
|
"aliases nativos: cs|csharp -> dotnet; c++|cxx|cc -> cpp (CLI e sema.config.json)",
|
|
124
|
-
"verificação final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--alvo <alvo>] [--sem-cache] [--json]",
|
|
125
|
+
"verificação final: sema verificar <arquivo-ou-pasta> [--saida <diretorio-base>] [--alvo <alvo>] [--sem-cache] [--preservar-implementacao] [--json]",
|
|
125
126
|
"formatação: sema formatar <arquivo-ou-pasta> [--check] [--json]",
|
|
126
127
|
"author: sema author <iniciar|validar|briefing|revisar-cliches|validar-narrativa|validar-proibicoes> [arquivo] [--tema-sensivel] [--preset conto|romance|roteiro|lore|campanha] [--saida <arquivo>] [--texto <texto>] [--json]",
|
|
127
128
|
"profile: sema profile validar <software|workflow|ops|game|simulation|legal|research|redacao|propostas|conversas> <arquivo-ou-pasta> [--maturidade draft|prototype|production|critical] [--preset <preset>] [--artefato <texto>|--artefato-arquivo <arquivo>] [--json]",
|
package/dist/geracaoCore.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface ResumoAlvoVerificacao {
|
|
|
18
18
|
estrutura: EstruturaSaida;
|
|
19
19
|
testesExecutados: boolean;
|
|
20
20
|
origem?: "executado" | "cache";
|
|
21
|
-
tipoEvidencia?: "andaime_demonstrativo";
|
|
21
|
+
tipoEvidencia?: "andaime_demonstrativo" | "implementacao_local_preservada";
|
|
22
22
|
implementacaoExternaExecutada?: boolean;
|
|
23
23
|
}
|
|
24
24
|
export interface ResumoModuloVerificacao {
|
package/dist/index.part07.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { EstruturaSaida } from "./tipos.js";
|
|
|
3
3
|
export interface OpcoesVerificar {
|
|
4
4
|
alvo?: string;
|
|
5
5
|
semCache?: boolean;
|
|
6
|
+
preservarImplementacao?: boolean;
|
|
6
7
|
}
|
|
7
8
|
export declare function alvosSuportadosPelaStack(fontesLegado: string[] | undefined): AlvoGeracao[] | undefined;
|
|
8
9
|
export declare function avisoEscopoStackVerificacao(fontesLegado: string[] | undefined, alvosEfetivos: AlvoGeracao[]): string | undefined;
|
package/dist/index.part07.js
CHANGED
|
@@ -6,6 +6,7 @@ import pacoteCli from "../package.json" with { type: "json" };
|
|
|
6
6
|
import { compilarCodigo, formatarDiagnosticos, lerArquivoTexto, temErros, } from "@sema/nucleo";
|
|
7
7
|
import { carregarProjeto, resolverAlvosVerificacao, } from "./projeto.js";
|
|
8
8
|
import { SCHEMA_CACHE_VERIFICACAO, calcularChaveVerificacao, carregarCacheVerificacao, gravarCacheVerificacao, } from "./verificacaoCache.js";
|
|
9
|
+
import { erroModoPreservacao, prepararImplementacaoPythonLocal, evidenciaPython } from "./verificacaoPythonLocal.js";
|
|
9
10
|
const FONTES_POR_ALVO_SUPORTADO = {
|
|
10
11
|
typescript: ["typescript", "javascript"],
|
|
11
12
|
javascript: ["typescript", "javascript"],
|
|
@@ -210,7 +211,7 @@ export function imprimirResumoVerificacao(resumos) {
|
|
|
210
211
|
totalArquivos += alvo.arquivosGerados;
|
|
211
212
|
totalTestes += alvo.quantidadeTestes;
|
|
212
213
|
totalAlvos += 1;
|
|
213
|
-
console.log(` alvo=${alvo.alvo} framework=${alvo.framework} estrutura=${alvo.estrutura} status=${alvo.sucesso ? "ok" : "falhou"} arquivos=${alvo.arquivosGerados} testes=${alvo.quantidadeTestes}${alvo.testesExecutados ? "" : " (skip)"} saida=${alvo.pastaSaida}`);
|
|
214
|
+
console.log(` alvo=${alvo.alvo} framework=${alvo.framework} estrutura=${alvo.estrutura} status=${alvo.sucesso ? "ok" : "falhou"} arquivos=${alvo.arquivosGerados} testes=${alvo.quantidadeTestes}${alvo.testesExecutados ? "" : " (skip)"}${alvo.tipoEvidencia ? ` evidencia=${alvo.tipoEvidencia} (não comprova produção)` : ""} saida=${alvo.pastaSaida}`);
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
console.log(`Totais: modulos=${resumos.length} alvos=${totalAlvos} arquivos=${totalArquivos} testes=${totalTestes}`);
|
|
@@ -240,6 +241,11 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
|
|
|
240
241
|
...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
|
|
241
242
|
}));
|
|
242
243
|
const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
|
|
244
|
+
const erroPreservacao = erroModoPreservacao(opcoes.preservarImplementacao, configuracoesAlvo);
|
|
245
|
+
if (erroPreservacao) {
|
|
246
|
+
console.error(erroPreservacao);
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
243
249
|
if (incompatibilidade?.incompatibilidade) {
|
|
244
250
|
console.error(incompatibilidade.incompatibilidade);
|
|
245
251
|
return 1;
|
|
@@ -272,7 +278,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
|
|
|
272
278
|
estrutura,
|
|
273
279
|
conteudoContrato,
|
|
274
280
|
});
|
|
275
|
-
const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
|
|
281
|
+
const emCache = opcoes.semCache || opcoes.preservarImplementacao ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
|
|
276
282
|
if (emCache) {
|
|
277
283
|
resumoModulo.alvos.push({
|
|
278
284
|
alvo,
|
|
@@ -294,21 +300,27 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
|
|
|
294
300
|
continue;
|
|
295
301
|
}
|
|
296
302
|
const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
|
|
297
|
-
const
|
|
303
|
+
const preparacao = opcoes.preservarImplementacao ? await prepararImplementacaoPythonLocal(pastaAlvo, arquivos) : { arquivosParaEscrita: arquivos };
|
|
304
|
+
if (preparacao.erro) {
|
|
305
|
+
console.error(preparacao.erro);
|
|
306
|
+
return 1;
|
|
307
|
+
}
|
|
308
|
+
const escrita = await escreverArquivos(pastaAlvo, preparacao.arquivosParaEscrita, { artefatoGerado: true });
|
|
298
309
|
artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
|
|
299
310
|
const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
if (!opcoes.preservarImplementacao)
|
|
312
|
+
await gravarCacheVerificacao({
|
|
313
|
+
schemaVersion: SCHEMA_CACHE_VERIFICACAO,
|
|
314
|
+
chave,
|
|
315
|
+
versaoCli: pacoteCli.version,
|
|
316
|
+
alvo,
|
|
317
|
+
modulo: ir.nome,
|
|
318
|
+
sucesso: execucao.codigoSaida === 0,
|
|
319
|
+
quantidadeTestes: execucao.quantidadeTestes,
|
|
320
|
+
testesExecutados,
|
|
321
|
+
arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
|
|
322
|
+
geradoEm: new Date().toISOString(),
|
|
323
|
+
});
|
|
312
324
|
resumoModulo.alvos.push({
|
|
313
325
|
alvo,
|
|
314
326
|
arquivosGerados: arquivos.length,
|
|
@@ -319,7 +331,7 @@ export async function comandoVerificar(entrada, baseSaida, cwd = process.cwd(),
|
|
|
319
331
|
estrutura,
|
|
320
332
|
testesExecutados,
|
|
321
333
|
origem: "executado",
|
|
322
|
-
...(alvo === "python" ?
|
|
334
|
+
...(alvo === "python" ? evidenciaPython(opcoes.preservarImplementacao) : {}),
|
|
323
335
|
});
|
|
324
336
|
if (execucao.codigoSaida !== 0) {
|
|
325
337
|
imprimirResumoVerificacao([...resumos, resumoModulo]);
|
|
@@ -372,6 +384,11 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
|
|
|
372
384
|
...resolverConfiguracaoVerificacaoPorAlvo(alvo, contextoProjeto.configCarregada),
|
|
373
385
|
}));
|
|
374
386
|
const incompatibilidade = configuracoesAlvo.find((item) => item.incompatibilidade);
|
|
387
|
+
const erroPreservacao = erroModoPreservacao(opcoes.preservarImplementacao, configuracoesAlvo);
|
|
388
|
+
if (erroPreservacao) {
|
|
389
|
+
console.log(JSON.stringify({ comando: "verificar", sucesso: false, erro: erroPreservacao, modulos: [], totais: { modulos: 0, alvos: 0, arquivos: 0, testes: 0 } }));
|
|
390
|
+
return 1;
|
|
391
|
+
}
|
|
375
392
|
if (incompatibilidade?.incompatibilidade) {
|
|
376
393
|
console.log(JSON.stringify({
|
|
377
394
|
comando: "verificar",
|
|
@@ -418,7 +435,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
|
|
|
418
435
|
estrutura,
|
|
419
436
|
conteudoContrato,
|
|
420
437
|
});
|
|
421
|
-
const emCache = opcoes.semCache ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
|
|
438
|
+
const emCache = opcoes.semCache || opcoes.preservarImplementacao ? undefined : await carregarCacheVerificacao(chave, pastaAlvo);
|
|
422
439
|
if (emCache) {
|
|
423
440
|
resumoModulo.alvos.push({
|
|
424
441
|
alvo,
|
|
@@ -439,21 +456,27 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
|
|
|
439
456
|
continue;
|
|
440
457
|
}
|
|
441
458
|
const arquivos = aplicarEstruturaSaida(gerarArquivosPorAlvo(ir, alvo, framework), ir, estrutura);
|
|
442
|
-
const
|
|
459
|
+
const preparacao = opcoes.preservarImplementacao ? await prepararImplementacaoPythonLocal(pastaAlvo, arquivos) : { arquivosParaEscrita: arquivos };
|
|
460
|
+
if (preparacao.erro) {
|
|
461
|
+
console.log(JSON.stringify({ comando: "verificar", sucesso: false, erro: preparacao.erro, modulos: resumos, totais: { modulos: resumos.length, alvos: 0, arquivos: 0, testes: 0 } }));
|
|
462
|
+
return 1;
|
|
463
|
+
}
|
|
464
|
+
const escrita = await escreverArquivos(pastaAlvo, preparacao.arquivosParaEscrita, { artefatoGerado: true });
|
|
443
465
|
artefatosGeradosAcimaDoLimite.push(...escrita.artefatosGeradosAcimaDoLimite);
|
|
444
466
|
const { execucao, testesExecutados } = executarTestesParaVerificacao(alvo, pastaAlvo, arquivos, framework, true);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
467
|
+
if (!opcoes.preservarImplementacao)
|
|
468
|
+
await gravarCacheVerificacao({
|
|
469
|
+
schemaVersion: SCHEMA_CACHE_VERIFICACAO,
|
|
470
|
+
chave,
|
|
471
|
+
versaoCli: pacoteCli.version,
|
|
472
|
+
alvo,
|
|
473
|
+
modulo: ir.nome,
|
|
474
|
+
sucesso: execucao.codigoSaida === 0,
|
|
475
|
+
quantidadeTestes: execucao.quantidadeTestes,
|
|
476
|
+
testesExecutados,
|
|
477
|
+
arquivosGerados: arquivos.map((arquivo) => arquivo.caminhoRelativo),
|
|
478
|
+
geradoEm: new Date().toISOString(),
|
|
479
|
+
});
|
|
457
480
|
resumoModulo.alvos.push({
|
|
458
481
|
alvo,
|
|
459
482
|
arquivosGerados: arquivos.length,
|
|
@@ -464,7 +487,7 @@ export async function comandoVerificarJson(entrada, baseSaida, cwd = process.cwd
|
|
|
464
487
|
estrutura,
|
|
465
488
|
testesExecutados,
|
|
466
489
|
origem: "executado",
|
|
467
|
-
...(alvo === "python" ?
|
|
490
|
+
...(alvo === "python" ? evidenciaPython(opcoes.preservarImplementacao) : {}),
|
|
468
491
|
});
|
|
469
492
|
resumoModulo.saidaTestes.push({ alvo, stdout: execucao.saidaPadrao, stderr: execucao.saidaErro });
|
|
470
493
|
if (execucao.codigoSaida !== 0) {
|
package/dist/index.part08.js
CHANGED
|
@@ -105,8 +105,8 @@ export async function principal() {
|
|
|
105
105
|
break;
|
|
106
106
|
case "verificar":
|
|
107
107
|
codigoSaida = possuiFlag(resto, "--json")
|
|
108
|
-
? await comandoVerificarJson(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache") })
|
|
109
|
-
: await comandoVerificar(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache") });
|
|
108
|
+
? await comandoVerificarJson(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache"), preservarImplementacao: possuiFlag(resto, "--preservar-implementacao") })
|
|
109
|
+
: await comandoVerificar(posicionais[0], resolverSaidaPadrao(obterOpcao(resto, "--saida", "./.tmp/sema-verificar"), "typescript", await carregarConfiguracaoProjeto(posicionais[0] ? path.resolve(cwd, posicionais[0]) : cwd)), cwd, { alvo: obterOpcao(resto, "--alvo"), semCache: possuiFlag(resto, "--sem-cache"), preservarImplementacao: possuiFlag(resto, "--preservar-implementacao") });
|
|
110
110
|
break;
|
|
111
111
|
case "inspecionar":
|
|
112
112
|
codigoSaida = await comandoInspecionar(posicionais[0], possuiFlag(resto, "--json"), cwd, resto);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type Arquivo = {
|
|
2
|
+
caminhoRelativo: string;
|
|
3
|
+
conteudo: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function erroModoPreservacao(preservar: boolean | undefined, configuracoes: Array<{
|
|
6
|
+
alvo: string;
|
|
7
|
+
framework: string;
|
|
8
|
+
}>): string | undefined;
|
|
9
|
+
export declare function prepararImplementacaoPythonLocal(base: string, arquivos: Arquivo[]): Promise<{
|
|
10
|
+
arquivosParaEscrita: Arquivo[];
|
|
11
|
+
erro?: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function evidenciaPython(preservar: boolean | undefined): {
|
|
14
|
+
tipoEvidencia: "andaime_demonstrativo" | "implementacao_local_preservada";
|
|
15
|
+
implementacaoExternaExecutada: boolean;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SEMA-GOVERNED: sema.produto.cli_verificacao
|
|
2
|
+
// Contrato: contratos/sema/cli_verificacao.sema
|
|
3
|
+
// Descrição: preserva Python local explicitamente; testes continuam derivados do contrato, sem cache.
|
|
4
|
+
import { lstat } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
export function erroModoPreservacao(preservar, configuracoes) {
|
|
7
|
+
if (preservar && (configuracoes.length !== 1 || configuracoes[0].alvo !== "python" || configuracoes[0].framework !== "base")) {
|
|
8
|
+
return "--preservar-implementacao exige --alvo python e framework base.";
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
export async function prepararImplementacaoPythonLocal(base, arquivos) {
|
|
13
|
+
const implementacoes = arquivos.filter(arquivo => arquivo.caminhoRelativo.endsWith(".py") && !path.basename(arquivo.caminhoRelativo).startsWith("test_"));
|
|
14
|
+
if (implementacoes.length === 0)
|
|
15
|
+
return { arquivosParaEscrita: [], erro: "Nenhuma implementação Python local prevista pelo contrato." };
|
|
16
|
+
try {
|
|
17
|
+
for (const arquivo of implementacoes) {
|
|
18
|
+
const destino = path.resolve(base, arquivo.caminhoRelativo);
|
|
19
|
+
const relativo = path.relative(path.resolve(base), destino);
|
|
20
|
+
if (path.isAbsolute(relativo) || relativo.split(path.sep).includes(".."))
|
|
21
|
+
throw new Error("escape");
|
|
22
|
+
let atual = destino;
|
|
23
|
+
while (true) {
|
|
24
|
+
const info = await lstat(atual);
|
|
25
|
+
if (info.isSymbolicLink() || (atual === destino ? !info.isFile() || info.nlink > 1 : !info.isDirectory()))
|
|
26
|
+
throw new Error("link ou tipo inválido");
|
|
27
|
+
const pai = path.dirname(atual);
|
|
28
|
+
if (pai === atual)
|
|
29
|
+
break;
|
|
30
|
+
atual = pai;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const preservados = new Set(implementacoes.map(arquivo => arquivo.caminhoRelativo));
|
|
34
|
+
return { arquivosParaEscrita: arquivos.filter(arquivo => !preservados.has(arquivo.caminhoRelativo)) };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { arquivosParaEscrita: [], erro: "Implementação Python local ausente ou caminho não seguro. Gere e preencha a implementação antes de usar --preservar-implementacao." };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function evidenciaPython(preservar) {
|
|
41
|
+
return {
|
|
42
|
+
tipoEvidencia: preservar ? "implementacao_local_preservada" : "andaime_demonstrativo",
|
|
43
|
+
implementacaoExternaExecutada: false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=verificacaoPythonLocal.js.map
|
package/docs/testing.md
CHANGED
|
@@ -169,3 +169,21 @@ this scaffold: its tests and guarantees cannot establish production behavior.
|
|
|
169
169
|
Use the application's real tests and drift evidence for that claim. Generated
|
|
170
170
|
failure cases currently retain the explicit synthetic error context and are
|
|
171
171
|
not evidence that the real implementation produces those errors.
|
|
172
|
+
|
|
173
|
+
To execute an explicitly filled local Python implementation without overwriting
|
|
174
|
+
it, use the same verification output base that contains the generated module:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
sema verificar contratos/app.sema --alvo python --saida .tmp/check --preservar-implementacao --json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This opt-in mode supports only Python with the base framework. All expected
|
|
181
|
+
implementation files must already exist as regular files, without symlinks,
|
|
182
|
+
junctions or hardlinks. It preserves those files byte for byte, regenerates the
|
|
183
|
+
contract tests and support artifacts, and always runs the tests without reading
|
|
184
|
+
or writing the verification cache. Missing implementations fail before writes;
|
|
185
|
+
new expectations and incorrect implementation results still fail. The output
|
|
186
|
+
reports `tipoEvidencia: implementacao_local_preservada` and
|
|
187
|
+
`implementacaoExternaExecutada: false`: it proves execution of those local files,
|
|
188
|
+
not automatic execution of a linked `impl`, a remote deployment or production.
|
|
189
|
+
Normal verification retains the demonstration scaffold behavior.
|
|
@@ -8,7 +8,7 @@ export declare function gerarComentarioInvariantesTypeScript(invariantes: Expres
|
|
|
8
8
|
export declare function gerarLiteralCamposTypeScript(campos: IrCampo[]): string;
|
|
9
9
|
export declare function coletarTiposExternos(modulo: IrModulo): string[];
|
|
10
10
|
export declare function gerarLiteralErrosTypeScript(erros: Record<string, string>): string;
|
|
11
|
-
export declare function formatarValorTypeScript(valor: string, camposConhecidos: Set<string>, variavel: string): string;
|
|
11
|
+
export declare function formatarValorTypeScript(valor: string, camposConhecidos: Set<string>, variavel: string, literal?: boolean): string;
|
|
12
12
|
export declare function resolverReferenciaTypeScript(referencia: string, variavel: string): string;
|
|
13
13
|
export declare function gerarExpressaoTypeScript(expressao: ExpressaoSemantica, camposConhecidos: Set<string>, variavel: string): string;
|
|
14
14
|
export declare function valorPadraoTypeScript(tipo: string, nomeCampo: string): string;
|
|
@@ -17,7 +17,7 @@ export declare function dividirLiteralNoNivelRaiz(valor: string, separador: ","
|
|
|
17
17
|
export declare function resolverTipoItemTeste(tipoDeclarado?: string): string | undefined;
|
|
18
18
|
export declare function formatarLiteralTesteTypeScript(valor: string, tipoDeclarado?: string): unknown;
|
|
19
19
|
export declare function converterBlocoTesteParaValorTypeScript(bloco: IrBlocoDeclarativo, tiposDeclarados?: Map<string, string>): Record<string, unknown>;
|
|
20
|
-
export declare function gerarPreparacaoSaida(task: IrTask): string;
|
|
20
|
+
export declare function gerarPreparacaoSaida(task: IrTask, tiposDeclarados?: Map<string, Map<string, string>>): string;
|
|
21
21
|
export declare function gerarValidacoes(task: IrTask): string;
|
|
22
22
|
export declare function gerarGarantias(task: IrTask): string;
|
|
23
23
|
export declare function gerarFuncaoGarantias(task: IrTask): string;
|
|
@@ -48,7 +48,7 @@ export function gerarLiteralErrosTypeScript(erros) {
|
|
|
48
48
|
}
|
|
49
49
|
return `{\n${entradas.map(([nome, mensagem]) => ` ${JSON.stringify(nome)}: ${JSON.stringify(mensagem)},`).join("\n")}\n}`;
|
|
50
50
|
}
|
|
51
|
-
export function formatarValorTypeScript(valor, camposConhecidos, variavel) {
|
|
51
|
+
export function formatarValorTypeScript(valor, camposConhecidos, variavel, literal = false) {
|
|
52
52
|
const texto = valor.trim();
|
|
53
53
|
if (/^-?\d+(?:\.\d+)?$/.test(texto)) {
|
|
54
54
|
return texto;
|
|
@@ -62,7 +62,7 @@ export function formatarValorTypeScript(valor, camposConhecidos, variavel) {
|
|
|
62
62
|
if (texto === "nulo") {
|
|
63
63
|
return "null";
|
|
64
64
|
}
|
|
65
|
-
if (camposConhecidos.has(texto.split(".")[0] ?? texto)) {
|
|
65
|
+
if (!literal && camposConhecidos.has(texto.split(".")[0] ?? texto)) {
|
|
66
66
|
return `${variavel}.${texto}`;
|
|
67
67
|
}
|
|
68
68
|
return JSON.stringify(texto);
|
|
@@ -260,29 +260,131 @@ export function converterBlocoTesteParaValorTypeScript(bloco, tiposDeclarados) {
|
|
|
260
260
|
}
|
|
261
261
|
return literal;
|
|
262
262
|
}
|
|
263
|
-
|
|
263
|
+
function achatarGarantiasConjuntivasTypeScript(expressao) {
|
|
264
|
+
return expressao.tipo === "composta" && expressao.operadorLogico === "e"
|
|
265
|
+
? expressao.termos.flatMap(achatarGarantiasConjuntivasTypeScript)
|
|
266
|
+
: [expressao];
|
|
267
|
+
}
|
|
268
|
+
function sugerirValoresTypeScript(tipo, tiposDeclarados) {
|
|
269
|
+
if (["Numero", "Inteiro", "Decimal"].includes(tipo))
|
|
270
|
+
return "[1, 0, -1]";
|
|
271
|
+
if (tipo === "Booleano")
|
|
272
|
+
return "[false, true]";
|
|
273
|
+
if (["Texto", "Id", "Email", "Url", "Data", "DataHora", "Timestamp"].includes(tipo)) {
|
|
274
|
+
return '["valor_garantido", "outro_valor"]';
|
|
275
|
+
}
|
|
276
|
+
if (/^Lista</u.test(tipo) || /\[\]$/u.test(tipo))
|
|
277
|
+
return "[[]]";
|
|
278
|
+
if (/^Mapa</u.test(tipo) || ["Json", "Objeto"].includes(tipo) || tiposDeclarados.has(tipo))
|
|
279
|
+
return "[{}]";
|
|
280
|
+
return "[1, 0, -1, true, false, \"valor_garantido\", \"outro_valor\", {}]";
|
|
281
|
+
}
|
|
282
|
+
export function gerarPreparacaoSaida(task, tiposDeclarados = new Map()) {
|
|
264
283
|
const camposSaida = new Set(task.output.map((campo) => campo.nome));
|
|
265
284
|
const linhas = [];
|
|
266
285
|
for (const campo of task.output) {
|
|
267
286
|
linhas.push(` ${campo.nome}: ${valorPadraoTypeScript(campo.tipo, campo.nome)},`);
|
|
268
287
|
}
|
|
269
|
-
const
|
|
270
|
-
for (const garantia of task.garantiasEstruturadas) {
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
ajustes.push(` ${resolverReferenciaTypeScript(garantia.alvo, "saida")} = ${formatarValorTypeScript(garantia.valor, camposSaida, "saida")} as any;`);
|
|
288
|
+
const grupos = new Map();
|
|
289
|
+
for (const garantia of task.garantiasEstruturadas.flatMap(achatarGarantiasConjuntivasTypeScript)) {
|
|
290
|
+
if (!("alvo" in garantia) || !camposSaida.has(garantia.alvo.split(".")[0] ?? garantia.alvo))
|
|
291
|
+
continue;
|
|
292
|
+
if (garantia.tipo === "comparacao" || garantia.tipo === "pertencimento" || garantia.tipo === "existe") {
|
|
293
|
+
grupos.set(garantia.alvo, [...(grupos.get(garantia.alvo) ?? []), garantia]);
|
|
276
294
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
295
|
+
}
|
|
296
|
+
const referencias = new Set();
|
|
297
|
+
for (const regras of grupos.values()) {
|
|
298
|
+
for (const regra of regras) {
|
|
299
|
+
if (regra.tipo === "comparacao" && !regra.valorLiteral && camposSaida.has(regra.valor.split(".")[0] ?? regra.valor)) {
|
|
300
|
+
referencias.add(regra.valor);
|
|
282
301
|
}
|
|
283
302
|
}
|
|
284
303
|
}
|
|
285
|
-
|
|
304
|
+
for (const referencia of referencias) {
|
|
305
|
+
if (!grupos.has(referencia)) {
|
|
306
|
+
grupos.set(referencia, [{ tipo: "existe", alvo: referencia, textoOriginal: `${referencia} existe` }]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const caminhos = [...grupos.keys()].sort((a, b) => Number(referencias.has(b)) - Number(referencias.has(a)) || a.split(".").length - b.split(".").length);
|
|
310
|
+
const ajustes = [];
|
|
311
|
+
for (const caminho of caminhos) {
|
|
312
|
+
const regras = grupos.get(caminho);
|
|
313
|
+
let tipo = task.output.find((campo) => campo.nome === caminho.split(".")[0])?.tipo ?? "Json";
|
|
314
|
+
for (const segmento of caminho.split(".").slice(1)) {
|
|
315
|
+
tipo = tiposDeclarados.get(tipo)?.get(segmento) ?? "Json";
|
|
316
|
+
}
|
|
317
|
+
const restricoes = regras.map((regra) => {
|
|
318
|
+
if (regra.tipo === "comparacao") {
|
|
319
|
+
return `[${JSON.stringify(regra.operador)}, ${formatarValorTypeScript(regra.valor, camposSaida, "saida", regra.valorLiteral)}]`;
|
|
320
|
+
}
|
|
321
|
+
if (regra.tipo === "pertencimento") {
|
|
322
|
+
const valores = (regra.valores ?? []).map((valor) => formatarValorTypeScript(valor, camposSaida, "saida", true));
|
|
323
|
+
return `["in", [${valores.join(", ")}]]`;
|
|
324
|
+
}
|
|
325
|
+
return '["existe", null]';
|
|
326
|
+
});
|
|
327
|
+
const caminhoLiteral = JSON.stringify(caminho.split("."));
|
|
328
|
+
const somenteExistencia = regras.every((regra) => regra.tipo === "existe");
|
|
329
|
+
const temDescendente = caminhos.some((outro) => outro.startsWith(`${caminho}.`));
|
|
330
|
+
if (somenteExistencia && temDescendente) {
|
|
331
|
+
ajustes.push(` if (semaLerSaida(saida, ${caminhoLiteral}) == null) semaDefinirSaida(saida, ${caminhoLiteral}, {});`);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const inteiro = tipo === "Inteiro" ? "true" : "false";
|
|
335
|
+
ajustes.push(` semaDefinirSaida(saida, ${caminhoLiteral}, semaSintetizarSaida(semaLerSaida(saida, ${caminhoLiteral}), [${restricoes.join(", ")}], ${inteiro}, ${sugerirValoresTypeScript(tipo, tiposDeclarados)}));`);
|
|
336
|
+
}
|
|
337
|
+
const helpers = grupos.size === 0 ? "" : `
|
|
338
|
+
const semaLerSaida = (objeto: any, caminho: string[]): any => caminho.reduce((atual, campo) => atual == null ? undefined : atual[campo], objeto);
|
|
339
|
+
const semaDefinirSaida = (objeto: any, caminho: string[], valor: any): void => {
|
|
340
|
+
let atual = objeto;
|
|
341
|
+
for (const campo of caminho.slice(0, -1)) {
|
|
342
|
+
if (atual[campo] === null || typeof atual[campo] !== "object") atual[campo] = {};
|
|
343
|
+
atual = atual[campo];
|
|
344
|
+
}
|
|
345
|
+
const ultimo = caminho[caminho.length - 1];
|
|
346
|
+
if (ultimo !== undefined) atual[ultimo] = valor;
|
|
347
|
+
};
|
|
348
|
+
const semaSintetizarSaida = (atual: any, restricoes: Array<[string, any]>, inteiro: boolean, sementes: any[]): any => {
|
|
349
|
+
const aceita = (valor: any): boolean => restricoes.every(([operador, limite]) => {
|
|
350
|
+
if (operador === "existe") return valor !== undefined && valor !== null;
|
|
351
|
+
if (operador === "in") return Array.isArray(limite) && limite.some((item: any) => Object.is(item, valor));
|
|
352
|
+
if (operador === "==") return valor === limite;
|
|
353
|
+
if (operador === "!=") return valor !== limite;
|
|
354
|
+
const esquerda: any = valor;
|
|
355
|
+
const direita: any = limite;
|
|
356
|
+
if (operador === ">") return esquerda > direita;
|
|
357
|
+
if (operador === ">=") return esquerda >= direita;
|
|
358
|
+
if (operador === "<") return esquerda < direita;
|
|
359
|
+
if (operador === "<=") return esquerda <= direita;
|
|
360
|
+
return false;
|
|
361
|
+
});
|
|
362
|
+
const candidatos: any[] = [atual, ...sementes];
|
|
363
|
+
const numeros: number[] = [];
|
|
364
|
+
for (const [operador, limite] of restricoes) {
|
|
365
|
+
if (operador === "in" && Array.isArray(limite)) candidatos.push(...limite);
|
|
366
|
+
else if (operador !== "existe") {
|
|
367
|
+
candidatos.push(limite);
|
|
368
|
+
if (typeof limite === "number" && Number.isFinite(limite)) {
|
|
369
|
+
numeros.push(limite);
|
|
370
|
+
candidatos.push(limite - 1, limite + 1, Math.floor(limite), Math.ceil(limite));
|
|
371
|
+
} else if (typeof limite === "string") {
|
|
372
|
+
candidatos.push(limite + "\\u0000", limite.slice(0, -1), "");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
numeros.sort((a, b) => a - b);
|
|
377
|
+
for (let indice = 0; indice < numeros.length - 1; indice += 1) {
|
|
378
|
+
candidatos.push((numeros[indice]! + numeros[indice + 1]!) / 2);
|
|
379
|
+
}
|
|
380
|
+
for (const candidato of candidatos) {
|
|
381
|
+
if (inteiro && (typeof candidato !== "number" || !Number.isInteger(candidato))) continue;
|
|
382
|
+
if (aceita(candidato)) return candidato;
|
|
383
|
+
}
|
|
384
|
+
throw new Error("Nao foi possivel sintetizar uma saida demonstrativa compativel com as garantias.");
|
|
385
|
+
};
|
|
386
|
+
`;
|
|
387
|
+
return ` const saida = {\n${linhas.join("\n")}\n } as ${task.nome}Saida;${helpers}\n${ajustes.join("\n")}`;
|
|
286
388
|
}
|
|
287
389
|
export function gerarValidacoes(task) {
|
|
288
390
|
const linhas = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IrModulo, IrTask } from "@sema/nucleo";
|
|
2
2
|
import { type ArquivoGerado } from "@sema/padroes";
|
|
3
3
|
export declare function gerarRotas(modulo: IrModulo): string;
|
|
4
|
-
export declare function gerarTask(task: IrTask): string;
|
|
4
|
+
export declare function gerarTask(task: IrTask, tiposDeclarados?: Map<string, Map<string, string>>): string;
|
|
5
5
|
export declare function gerarTestes(modulo: IrModulo): string;
|
|
6
6
|
export type TipoCabecalhoSemaTypeScript = "contrato" | "teste" | "dto" | "service" | "controller" | "spec";
|
|
7
7
|
export declare function gerarCabecalhoSemaTypeScript(modulo: IrModulo, tipo?: TipoCabecalhoSemaTypeScript): string;
|
|
@@ -68,7 +68,7 @@ ${verificacoesErro || " throw erro;"}
|
|
|
68
68
|
`;
|
|
69
69
|
}).join("\n");
|
|
70
70
|
}
|
|
71
|
-
export function gerarTask(task) {
|
|
71
|
+
export function gerarTask(task, tiposDeclarados = new Map()) {
|
|
72
72
|
const nomeSimbolo = normalizarNomeParaSimbolo(task.nome);
|
|
73
73
|
const entradaNome = `${nomeSimbolo}_entrada`;
|
|
74
74
|
const saidaNome = `${nomeSimbolo}_saida`;
|
|
@@ -130,7 +130,7 @@ ${task.stateContract ? ` // Vinculo de estado: ${task.stateContract.nomeEstado
|
|
|
130
130
|
${task.implementacoesExternas.length > 0 ? ` // Implementacoes externas vinculadas:\n${task.implementacoesExternas.map((impl) => ` // - ${impl.origem}: ${impl.caminho} [${impl.statusImpl ?? "nao_verificado"}]`).join("\n")}` : ""}
|
|
131
131
|
// Efeitos declarados:
|
|
132
132
|
${task.efeitosEstruturados.map((efeito) => ` // - categoria=${efeito.categoria} alvo=${efeito.alvo}${efeito.detalhe ? ` detalhe=${efeito.detalhe}` : ""}${efeito.criticidade ? ` criticidade=${efeito.criticidade}` : ""}`).join("\n") || task.effects.map((efeito) => ` // - ${efeito}`).join("\n") || " // - Nenhum efeito declarado."}
|
|
133
|
-
${gerarPreparacaoSaida(task)}
|
|
133
|
+
${gerarPreparacaoSaida(task, tiposDeclarados)}
|
|
134
134
|
${gerarGarantias(task)}
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -211,6 +211,13 @@ export function gerarCabecalhoSemaTypeScript(modulo, tipo = "contrato") {
|
|
|
211
211
|
}
|
|
212
212
|
export function gerarTypeScriptBase(modulo) {
|
|
213
213
|
const nomeBase = normalizarNomeModulo(modulo.nome).replace(/\./g, "_");
|
|
214
|
+
const mapaTiposDeclarados = new Map();
|
|
215
|
+
for (const entidade of modulo.entities) {
|
|
216
|
+
mapaTiposDeclarados.set(entidade.nome, new Map(entidade.campos.map((campo) => [campo.nome, campo.tipo])));
|
|
217
|
+
}
|
|
218
|
+
for (const tipo of modulo.types) {
|
|
219
|
+
mapaTiposDeclarados.set(tipo.nome, new Map(tipo.definicao.campos.map((campo) => [campo.nome, campo.tipo])));
|
|
220
|
+
}
|
|
214
221
|
const interoperabilidades = modulo.interoperabilidades
|
|
215
222
|
.map((interop) => `// Interop externo ${interop.origem}: ${interop.caminho}`)
|
|
216
223
|
.join("\n");
|
|
@@ -235,7 +242,7 @@ export function gerarTypeScriptBase(modulo) {
|
|
|
235
242
|
const routes = modulo.routes
|
|
236
243
|
.map((route) => `// Route ${route.nome}: metodo=${route.metodo ?? "nao_definido"} caminho=${route.caminho ?? "nao_definido"} task=${route.task ?? "nao_definida"} input_publico=${route.inputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} output_publico=${route.outputPublico.map((campo) => campo.nome).join(", ") || "padrao_task"} erros_publicos=${route.errosPublicos.map((erro) => erro.nome).join(", ") || "padrao_task"} effects_publicos=${route.efeitosPublicos.map((efeito) => `${efeito.categoria}:${efeito.alvo}`).join(", ") || "nenhum"} garantias_publicas=${route.garantiasPublicasMinimas.length}`)
|
|
237
244
|
.join("\n");
|
|
238
|
-
const tasks = modulo.tasks.map(gerarTask).join("\n");
|
|
245
|
+
const tasks = modulo.tasks.map((task) => gerarTask(task, mapaTiposDeclarados)).join("\n");
|
|
239
246
|
const contratosPublicos = gerarRotas(modulo);
|
|
240
247
|
const codigo = `${gerarCabecalhoSemaTypeScript(modulo)}${interoperabilidades ? `${interoperabilidades}\n` : ""}\n${tiposExternos}\n${tipos}\n${entidades}\n${enums}\n${states}\n${flows}\n${routes}\n${tasks}\n${contratosPublicos}\n`;
|
|
241
248
|
const testes = `${gerarCabecalhoSemaTypeScript(modulo, "teste")}${gerarTestes(modulo)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semacode/cli",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "Local-first semantic governance CLI for AI agents, proven with Claude, Codex, zCode (GLM) and Kimi: contracts, drift, impact, docs gates, and code generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"icon": "logo.png",
|