@semacode/cli 2.2.0 → 2.3.1
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/LICENSE +39 -39
- package/README.md +251 -141
- package/dist/agentContext.d.ts +1 -1
- package/dist/agentContextPack.js +6 -1
- package/dist/agentContextTipos.d.ts +4 -2
- package/dist/agentContextTipos.js +7 -0
- package/dist/agentEntryPoints.js +551 -499
- package/dist/angular-consumer-standalone.d.ts +3 -1
- package/dist/angular-consumer-standalone.js +12 -8
- package/dist/comandos.js +4 -0
- package/dist/discovery/catalog.d.ts +11 -0
- package/dist/discovery/catalog.js +521 -0
- package/dist/discovery/command.d.ts +11 -0
- package/dist/discovery/command.js +226 -0
- package/dist/discovery/index.d.ts +5 -0
- package/dist/discovery/index.js +8 -0
- package/dist/discovery/ranker.d.ts +3 -0
- package/dist/discovery/ranker.js +310 -0
- package/dist/discovery/redaction.d.ts +2 -0
- package/dist/discovery/redaction.js +21 -0
- package/dist/discovery/render.d.ts +2 -0
- package/dist/discovery/render.js +100 -0
- package/dist/discovery/types.d.ts +119 -0
- package/dist/discovery/types.js +13 -0
- package/dist/drift.part01.d.ts +32 -2
- package/dist/drift.part01.js +2 -0
- package/dist/drift.part04.d.ts +3 -2
- package/dist/drift.part04.js +6 -3
- package/dist/drift.part05.d.ts +2 -1
- package/dist/drift.part05.js +4 -3
- package/dist/drift.part06.d.ts +2 -1
- package/dist/drift.part06.js +9 -5
- package/dist/drift.part07.d.ts +11 -10
- package/dist/drift.part07.js +40 -30
- package/dist/drift.part08.d.ts +2 -1
- package/dist/drift.part08.js +4 -3
- package/dist/drift.part11.js +71 -6
- package/dist/drift.part12.js +14 -1
- package/dist/drift.part13.d.ts +42 -7
- package/dist/drift.part13.js +660 -36
- package/dist/driftCache.d.ts +49 -0
- package/dist/driftCache.js +311 -0
- package/dist/driftCacheIncremental.d.ts +98 -0
- package/dist/driftCacheIncremental.js +596 -0
- package/dist/driftCatalogo.d.ts +91 -0
- package/dist/driftCatalogo.js +657 -0
- package/dist/driftCatalogoPersistente.d.ts +8 -0
- package/dist/driftCatalogoPersistente.js +77 -0
- package/dist/driftEscopo.d.ts +23 -0
- package/dist/driftEscopo.js +563 -0
- package/dist/driftEscopoReferencias.d.ts +4 -0
- package/dist/driftEscopoReferencias.js +265 -0
- package/dist/driftOrcamento.d.ts +12 -0
- package/dist/driftOrcamento.js +19 -3
- package/dist/execucoesExternas.js +5 -19
- package/dist/exemplosOficiais.js +29 -8
- package/dist/iaPrompts.d.ts +1 -1
- package/dist/iaPrompts.js +310 -307
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -5
- package/dist/index.part01.d.ts +17 -2
- package/dist/index.part01.js +119 -8
- package/dist/index.part02.js +21 -21
- package/dist/index.part03.d.ts +2 -2
- package/dist/index.part03.js +14 -3
- package/dist/index.part04.d.ts +2 -2
- package/dist/index.part04.js +113 -101
- package/dist/index.part05.d.ts +1 -1
- package/dist/index.part05.js +99 -22
- package/dist/index.part06.js +6 -2
- package/dist/index.part07.js +24 -24
- package/dist/index.part08.js +1 -1
- package/dist/init/templates.part01.js +393 -393
- package/dist/init/templates.part02.js +189 -189
- package/dist/initTemplatesBackend.js +73 -73
- package/dist/initTemplatesBase.js +81 -80
- package/dist/initTemplatesFlutter.js +137 -137
- package/dist/initTemplatesRuntime.js +350 -350
- package/dist/initTemplatesWeb.js +309 -309
- package/dist/pipelineConteudo/command.js +1 -36
- package/dist/profileArtefatoValidadores.d.ts +3 -2
- package/dist/profileArtefatoValidadores.js +81 -0
- package/dist/profileAuthorTipos.d.ts +4 -3
- package/dist/profileCatalogo.d.ts +5 -5
- package/dist/profileCatalogo.js +30 -0
- package/dist/profileCommand.d.ts +8 -2
- package/dist/profileCommand.js +15 -5
- package/dist/profileRegras.d.ts +5 -5
- package/dist/profileRegras.js +13 -1
- package/dist/profileRulePacks.js +68 -0
- package/dist/projeto.d.ts +1 -1
- package/dist/projetoCarregar.d.ts +2 -2
- package/dist/projetoCarregar.js +47 -24
- package/dist/projetoCatalogo.d.ts +7 -0
- package/dist/projetoCatalogo.js +125 -0
- package/dist/projetoTipos.d.ts +14 -5
- package/dist/sistemasInterativos/adapterProtocol.d.ts +3 -0
- package/dist/sistemasInterativos/adapterProtocol.js +240 -0
- package/dist/sistemasInterativos/advancedCatalog.d.ts +5 -0
- package/dist/sistemasInterativos/advancedCatalog.js +442 -0
- package/dist/sistemasInterativos/autonomy.d.ts +230 -0
- package/dist/sistemasInterativos/autonomy.js +753 -0
- package/dist/sistemasInterativos/canonical.d.ts +3 -0
- package/dist/sistemasInterativos/canonical.js +43 -0
- package/dist/sistemasInterativos/catalog.d.ts +116 -0
- package/dist/sistemasInterativos/catalog.js +543 -0
- package/dist/sistemasInterativos/command.d.ts +7 -0
- package/dist/sistemasInterativos/command.js +311 -0
- package/dist/sistemasInterativos/controlRun.d.ts +56 -0
- package/dist/sistemasInterativos/controlRun.js +391 -0
- package/dist/sistemasInterativos/evidence.d.ts +5 -0
- package/dist/sistemasInterativos/evidence.js +306 -0
- package/dist/sistemasInterativos/experienceIr.d.ts +12 -0
- package/dist/sistemasInterativos/experienceIr.js +895 -0
- package/dist/sistemasInterativos/experienceIrSemantics.d.ts +30 -0
- package/dist/sistemasInterativos/experienceIrSemantics.js +368 -0
- package/dist/sistemasInterativos/experienceIrTypes.d.ts +370 -0
- package/dist/sistemasInterativos/experienceIrTypes.js +5 -0
- package/dist/sistemasInterativos/extensionCommand.d.ts +391 -0
- package/dist/sistemasInterativos/extensionCommand.js +383 -0
- package/dist/sistemasInterativos/index.d.ts +19 -0
- package/dist/sistemasInterativos/index.js +22 -0
- package/dist/sistemasInterativos/operations.d.ts +40 -0
- package/dist/sistemasInterativos/operations.js +1167 -0
- package/dist/sistemasInterativos/operationsTypes.d.ts +297 -0
- package/dist/sistemasInterativos/operationsTypes.js +11 -0
- package/dist/sistemasInterativos/planner.d.ts +2 -0
- package/dist/sistemasInterativos/planner.js +159 -0
- package/dist/sistemasInterativos/portability.d.ts +164 -0
- package/dist/sistemasInterativos/portability.js +539 -0
- package/dist/sistemasInterativos/render.d.ts +3 -0
- package/dist/sistemasInterativos/render.js +179 -0
- package/dist/sistemasInterativos/temporal.d.ts +183 -0
- package/dist/sistemasInterativos/temporal.js +543 -0
- package/dist/sistemasInterativos/types.d.ts +215 -0
- package/dist/sistemasInterativos/types.js +4 -0
- package/dist/sistemasInterativos/validator.d.ts +2 -0
- package/dist/sistemasInterativos/validator.js +305 -0
- package/dist/sync/index.js +35 -35
- package/docs/README.md +34 -28
- package/docs/ai-onboarding.md +62 -62
- package/docs/ai-workflow.md +9 -4
- package/docs/auth.md +26 -26
- package/docs/cli.md +79 -79
- package/docs/commands.md +47 -2
- package/docs/database.md +15 -15
- package/docs/deploy.md +41 -41
- package/docs/descoberta-capacidades.md +106 -0
- package/docs/documentation.md +39 -39
- package/docs/env.md +16 -16
- package/docs/frontend.md +41 -41
- package/docs/getting-started.md +57 -57
- package/docs/i18n.md +15 -15
- package/docs/pipeline-conteudo.md +246 -246
- package/docs/profiles.md +54 -45
- package/docs/repositories.md +43 -43
- package/docs/rollback.md +31 -31
- package/docs/security.md +32 -32
- package/docs/sistemas-interativos.md +376 -0
- package/docs/support.md +8 -8
- package/docs/testing.md +51 -35
- package/docs/vocabulary.md +29 -29
- package/exemplos/agendamento.sema +1 -1
- package/exemplos/assinatura.sema +1 -1
- package/exemplos/auditoria.sema +1 -1
- package/exemplos/autenticacao.sema +1 -1
- package/exemplos/author_obra_comum.sema +1 -1
- package/exemplos/author_tema_sensivel.sema +1 -1
- package/exemplos/automacao.sema +1 -1
- package/exemplos/cadastro_usuario.sema +1 -1
- package/exemplos/calculadora.sema +1 -1
- package/exemplos/crud_simples.sema +1 -1
- package/exemplos/estoque.sema +1 -1
- package/exemplos/exportacao.sema +1 -1
- package/exemplos/fila.sema +1 -1
- package/exemplos/integracao_externa.sema +1 -1
- package/exemplos/multi_tenant.sema +1 -1
- package/exemplos/notificacao.sema +1 -1
- package/exemplos/operacao_estrategia.sema +132 -132
- package/exemplos/operacao_estrategia_discovery.sema +220 -220
- package/exemplos/operacao_estrategia_fluxo.sema +114 -114
- package/exemplos/operacao_estrategia_gates.sema +106 -106
- package/exemplos/operacao_estrategia_quarentena.sema +133 -133
- package/exemplos/operacao_estrategia_rollout.sema +124 -124
- package/exemplos/pagamento.sema +1 -1
- package/exemplos/pagamento_dominio.sema +1 -1
- package/exemplos/pedido.sema +1 -1
- package/exemplos/permissao.sema +1 -1
- package/exemplos/persistencia_vendor_first.sema +1 -1
- package/exemplos/pipeline-conteudo/README.md +44 -44
- package/exemplos/pipeline-conteudo/alvos.json +23 -23
- package/exemplos/pipeline-conteudo/definicao.json +136 -136
- package/exemplos/profile_conversas.sema +165 -165
- package/exemplos/profile_game.sema +1 -1
- package/exemplos/profile_legal.sema +1 -1
- package/exemplos/profile_ops.sema +1 -1
- package/exemplos/profile_research.sema +1 -1
- package/exemplos/profile_simulation.sema +211 -0
- package/exemplos/profile_software.sema +1 -1
- package/exemplos/profile_workflow_n8n.sema +1 -1
- package/exemplos/relatorio.sema +1 -1
- package/exemplos/replica_analitica_erp.sema +1 -1
- package/exemplos/sistemas-interativos/README.md +100 -0
- package/exemplos/sistemas-interativos/advanced/acceptance-context-evaluate-valid.json +5 -0
- package/exemplos/sistemas-interativos/advanced/acceptance-lock-valid.json +16 -0
- package/exemplos/sistemas-interativos/advanced/asset-provenance-valid.json +32 -0
- package/exemplos/sistemas-interativos/advanced/autonomy-repair-valid.json +93 -0
- package/exemplos/sistemas-interativos/advanced/control-run-definition-valid.json +78 -0
- package/exemplos/sistemas-interativos/advanced/control-run-valid.json +27 -0
- package/exemplos/sistemas-interativos/advanced/distributed-workers-valid.json +79 -0
- package/exemplos/sistemas-interativos/advanced/editor-state-valid.json +42 -0
- package/exemplos/sistemas-interativos/advanced/engine-snapshot-after-valid.json +32 -0
- package/exemplos/sistemas-interativos/advanced/engine-snapshot-before-valid.json +23 -0
- package/exemplos/sistemas-interativos/advanced/job-orchestration-valid.json +39 -0
- package/exemplos/sistemas-interativos/advanced/multimodal-evidence-valid.json +46 -0
- package/exemplos/sistemas-interativos/advanced/multiplayer-authority-valid.json +39 -0
- package/exemplos/sistemas-interativos/advanced/playtest-fuzz-valid.json +37 -0
- package/exemplos/sistemas-interativos/advanced/portability-valid.json +104 -0
- package/exemplos/sistemas-interativos/advanced/temporal-evidence-valid.json +27 -0
- package/exemplos/sistemas-interativos/advanced/temporal-valid.json +118 -0
- package/exemplos/sistemas-interativos/experience-ir-valid.json +459 -0
- package/exemplos/sistemas-interativos/game-2d.json +83 -0
- package/exemplos/sistemas-interativos/game-3d-human.json +81 -0
- package/exemplos/sistemas-interativos/game-pixel-16-bit.json +97 -0
- package/exemplos/sistemas-interativos/game-pixel-8-bit.json +94 -0
- package/exemplos/sistemas-interativos/game-xr-human.json +80 -0
- package/exemplos/sistemas-interativos/hybrid-2-5d.json +108 -0
- package/exemplos/sistemas-interativos/protocol-mutating-rollback-valid.json +75 -0
- package/exemplos/sistemas-interativos/protocol-read-only-valid.json +58 -0
- package/exemplos/sistemas-interativos/simulation-2d-controlled.json +97 -0
- package/exemplos/sistemas-interativos/simulation-3d-calibrated-autonomous.json +141 -0
- package/exemplos/sistemas-interativos/simulation-headless-autonomous-batch.json +112 -0
- package/exemplos/sistemas-interativos/simulation-pixel-16-bit.json +103 -0
- package/exemplos/sistemas-interativos/simulation-text-controlled.json +95 -0
- package/exemplos/testes_embutidos.sema +1 -1
- package/exemplos/tratamento_erro.sema +1 -1
- package/exemplos/upload_arquivo.sema +1 -1
- package/exemplos/webhook.sema +1 -1
- package/node_modules/@sema/gerador-cpp/dist/index.js +176 -176
- package/node_modules/@sema/gerador-cpp/package.json +1 -1
- package/node_modules/@sema/gerador-css/dist/index.part01.js +10 -10
- package/node_modules/@sema/gerador-css/dist/index.part02.js +460 -460
- package/node_modules/@sema/gerador-dotnet/dist/index.js +169 -169
- package/node_modules/@sema/gerador-dotnet/package.json +1 -1
- package/node_modules/@sema/gerador-html/dist/index.js +17 -17
- package/node_modules/@sema/gerador-lua/dist/index.js +76 -47
- package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -1
- package/node_modules/@sema/gerador-php/dist/index.js +113 -113
- package/node_modules/@sema/gerador-python/dist/index.part02.js +3 -3
- package/node_modules/@sema/gerador-typescript/dist/index.part02.js +11 -11
- package/node_modules/@sema/nucleo/dist/diagnosticos/melhorador.js +42 -42
- package/package.json +3 -3
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// SEMA-GOVERNED: sema.produto.governanca_ia.drift
|
|
2
|
+
// Descricao: cache persistente incremental por arquivo e por grupo semantico, com validacao runtime e fast path por metadados.
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { lstat, readFile, realpath } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { digestJsonCanonicoCacheDrift, serializarJsonCanonicoCacheDrift, } from "./driftCache.js";
|
|
7
|
+
import { escreverArquivoWorkspaceSeguro, validarDestinosEscritaWorkspace, } from "./workspaceWrite.js";
|
|
8
|
+
export const FILE_INDEX_SCHEMA = 2;
|
|
9
|
+
export const SCHEMA_MANIFESTO_ARQUIVOS_DRIFT = "sema.drift-file-cache/v2";
|
|
10
|
+
export const SCHEMA_GRUPO_INDICE_DRIFT = "sema.drift-index-group-cache/v2";
|
|
11
|
+
export const DIRETORIO_CACHE_INCREMENTAL_DRIFT = path.join(".tmp", "sema-cache", "drift-index", "v2");
|
|
12
|
+
export const DIRETORIO_IDENTIDADES_CACHE_INCREMENTAL_DRIFT = path.join(DIRETORIO_CACHE_INCREMENTAL_DRIFT, "identities");
|
|
13
|
+
const LIMITE_BYTES_CACHE = 64 * 1024 * 1024;
|
|
14
|
+
const PADRAO_DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
15
|
+
function sufixoSeguroIdentidade(identidadeDigest) {
|
|
16
|
+
if (!PADRAO_DIGEST.test(identidadeDigest)) {
|
|
17
|
+
throw new TypeError(`identidade_digest_cache_drift_invalida:${identidadeDigest}`);
|
|
18
|
+
}
|
|
19
|
+
return identidadeDigest.slice("sha256:".length);
|
|
20
|
+
}
|
|
21
|
+
export function caminhoManifestoArquivosDrift(identidadeDigest) {
|
|
22
|
+
return path.join(DIRETORIO_IDENTIDADES_CACHE_INCREMENTAL_DRIFT, sufixoSeguroIdentidade(identidadeDigest), "files.json");
|
|
23
|
+
}
|
|
24
|
+
export function caminhoManifestoGruposDrift(identidadeDigest) {
|
|
25
|
+
return path.join(DIRETORIO_IDENTIDADES_CACHE_INCREMENTAL_DRIFT, sufixoSeguroIdentidade(identidadeDigest), "groups.json");
|
|
26
|
+
}
|
|
27
|
+
function registroObjeto(valor) {
|
|
28
|
+
return typeof valor === "object" && valor !== null && !Array.isArray(valor);
|
|
29
|
+
}
|
|
30
|
+
function emitirEvento(observador, evento) {
|
|
31
|
+
try {
|
|
32
|
+
observador?.(evento);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Observabilidade nao altera o resultado funcional do drift.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function paraJson(valor) {
|
|
39
|
+
return JSON.parse(JSON.stringify(valor));
|
|
40
|
+
}
|
|
41
|
+
function assinaturasIguais(a, b) {
|
|
42
|
+
return a.size === b.size
|
|
43
|
+
&& a.mtimeNs === b.mtimeNs
|
|
44
|
+
&& a.ctimeNs === b.ctimeNs
|
|
45
|
+
&& a.dev === b.dev
|
|
46
|
+
&& a.ino === b.ino;
|
|
47
|
+
}
|
|
48
|
+
function assinaturaValida(valor) {
|
|
49
|
+
if (!registroObjeto(valor)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return ["size", "mtimeNs", "ctimeNs", "dev", "ino"]
|
|
53
|
+
.every((campo) => typeof valor[campo] === "string" && /^\d+$/u.test(valor[campo]));
|
|
54
|
+
}
|
|
55
|
+
function caminhoRelativoSeguro(baseProjeto, arquivo) {
|
|
56
|
+
const relativo = path.relative(baseProjeto, path.resolve(arquivo)).replace(/\\/g, "/");
|
|
57
|
+
if (!relativo || relativo === "." || relativo === ".." || relativo.startsWith("../") || path.isAbsolute(relativo)) {
|
|
58
|
+
throw new Error(`arquivo_cache_drift_fora_workspace:${arquivo}`);
|
|
59
|
+
}
|
|
60
|
+
return relativo;
|
|
61
|
+
}
|
|
62
|
+
function caminhoRelativoValido(caminho) {
|
|
63
|
+
return typeof caminho === "string"
|
|
64
|
+
&& caminho.length > 0
|
|
65
|
+
&& caminho !== "."
|
|
66
|
+
&& caminho !== ".."
|
|
67
|
+
&& !caminho.startsWith("../")
|
|
68
|
+
&& !caminho.includes("\\")
|
|
69
|
+
&& !path.posix.isAbsolute(caminho);
|
|
70
|
+
}
|
|
71
|
+
function chaveEstavel(valor) {
|
|
72
|
+
return createHash("sha256")
|
|
73
|
+
.update(serializarJsonCanonicoCacheDrift(valor))
|
|
74
|
+
.digest("hex");
|
|
75
|
+
}
|
|
76
|
+
function analiseValida(valor) {
|
|
77
|
+
return registroObjeto(valor)
|
|
78
|
+
&& typeof valor.payloadDigest === "string"
|
|
79
|
+
&& PADRAO_DIGEST.test(valor.payloadDigest)
|
|
80
|
+
&& digestJsonCanonicoCacheDrift(valor.payload) === valor.payloadDigest;
|
|
81
|
+
}
|
|
82
|
+
function entradaArquivoValida(chave, valor) {
|
|
83
|
+
if (!registroObjeto(valor)
|
|
84
|
+
|| valor.caminho !== chave
|
|
85
|
+
|| !caminhoRelativoValido(valor.caminho)
|
|
86
|
+
|| !assinaturaValida(valor.assinatura)
|
|
87
|
+
|| typeof valor.contentHash !== "string"
|
|
88
|
+
|| !PADRAO_DIGEST.test(valor.contentHash)
|
|
89
|
+
|| !registroObjeto(valor.analises)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return Object.values(valor.analises).every(analiseValida);
|
|
93
|
+
}
|
|
94
|
+
function manifestoValido(valor, identidadeDigest) {
|
|
95
|
+
if (!registroObjeto(valor)
|
|
96
|
+
|| valor.identidadeDigest !== identidadeDigest
|
|
97
|
+
|| !registroObjeto(valor.arquivos)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return Object.entries(valor.arquivos).every(([chave, entrada]) => entradaArquivoValida(chave, entrada));
|
|
101
|
+
}
|
|
102
|
+
function membrosGrupoValidos(valor) {
|
|
103
|
+
return Array.isArray(valor)
|
|
104
|
+
&& valor.every((item) => registroObjeto(item)
|
|
105
|
+
&& caminhoRelativoValido(item.caminho)
|
|
106
|
+
&& typeof item.contentHash === "string"
|
|
107
|
+
&& PADRAO_DIGEST.test(item.contentHash));
|
|
108
|
+
}
|
|
109
|
+
function membrosIguais(a, b) {
|
|
110
|
+
return a.length === b.length
|
|
111
|
+
&& a.every((item, indice) => (item.caminho === b[indice]?.caminho
|
|
112
|
+
&& item.contentHash === b[indice]?.contentHash));
|
|
113
|
+
}
|
|
114
|
+
function entradaGrupoValida(valor, identidadeDigest) {
|
|
115
|
+
return registroObjeto(valor)
|
|
116
|
+
&& valor.identidadeDigest === identidadeDigest
|
|
117
|
+
&& membrosGrupoValidos(valor.membros)
|
|
118
|
+
&& typeof valor.resumoDigest === "string"
|
|
119
|
+
&& PADRAO_DIGEST.test(valor.resumoDigest)
|
|
120
|
+
&& digestJsonCanonicoCacheDrift(valor.resumo) === valor.resumoDigest;
|
|
121
|
+
}
|
|
122
|
+
function manifestoGruposValido(valor, identidadeDigest) {
|
|
123
|
+
return registroObjeto(valor)
|
|
124
|
+
&& valor.identidadeDigest === identidadeDigest
|
|
125
|
+
&& registroObjeto(valor.grupos)
|
|
126
|
+
&& Object.entries(valor.grupos).every(([chave, entrada]) => (/^[a-f0-9]{64}$/u.test(chave)
|
|
127
|
+
&& entradaGrupoValida(entrada, identidadeDigest)));
|
|
128
|
+
}
|
|
129
|
+
function erroEhAusencia(erro) {
|
|
130
|
+
return ["ENOENT", "ENOTDIR"].includes(erro.code ?? "");
|
|
131
|
+
}
|
|
132
|
+
async function lerEnvelope(baseProjeto, caminhoRelativo, schema, key) {
|
|
133
|
+
try {
|
|
134
|
+
const [destino] = await validarDestinosEscritaWorkspace(baseProjeto, [caminhoRelativo]);
|
|
135
|
+
if (!destino.existe) {
|
|
136
|
+
return { estado: "miss", motivo: "arquivo_ausente" };
|
|
137
|
+
}
|
|
138
|
+
const info = await lstat(destino.caminhoAbsoluto);
|
|
139
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > LIMITE_BYTES_CACHE) {
|
|
140
|
+
return { estado: "corrupt", motivo: "arquivo_cache_invalido" };
|
|
141
|
+
}
|
|
142
|
+
const bytes = await readFile(destino.caminhoAbsoluto);
|
|
143
|
+
if (bytes.byteLength > LIMITE_BYTES_CACHE) {
|
|
144
|
+
return { estado: "corrupt", motivo: "limite_bytes_excedido" };
|
|
145
|
+
}
|
|
146
|
+
let bruto;
|
|
147
|
+
try {
|
|
148
|
+
bruto = JSON.parse(bytes.toString("utf8"));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return { estado: "corrupt", motivo: "json_invalido" };
|
|
152
|
+
}
|
|
153
|
+
if (!registroObjeto(bruto)
|
|
154
|
+
|| bruto.schema !== schema
|
|
155
|
+
|| bruto.key !== key
|
|
156
|
+
|| typeof bruto.payloadDigest !== "string"
|
|
157
|
+
|| !PADRAO_DIGEST.test(bruto.payloadDigest)) {
|
|
158
|
+
return { estado: "corrupt", motivo: "envelope_invalido" };
|
|
159
|
+
}
|
|
160
|
+
let digest;
|
|
161
|
+
try {
|
|
162
|
+
digest = digestJsonCanonicoCacheDrift(bruto.payload);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return { estado: "corrupt", motivo: "payload_nao_canonico" };
|
|
166
|
+
}
|
|
167
|
+
if (digest !== bruto.payloadDigest) {
|
|
168
|
+
return { estado: "corrupt", motivo: "payload_digest_divergente" };
|
|
169
|
+
}
|
|
170
|
+
return { estado: "hit", payload: bruto.payload };
|
|
171
|
+
}
|
|
172
|
+
catch (erro) {
|
|
173
|
+
return {
|
|
174
|
+
estado: "miss",
|
|
175
|
+
motivo: erroEhAusencia(erro) ? "arquivo_ausente" : "cache_indisponivel",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function escreverEnvelope(baseProjeto, caminhoRelativo, schema, key, payload) {
|
|
180
|
+
const envelope = {
|
|
181
|
+
schema,
|
|
182
|
+
key,
|
|
183
|
+
payload,
|
|
184
|
+
payloadDigest: digestJsonCanonicoCacheDrift(payload),
|
|
185
|
+
};
|
|
186
|
+
const conteudo = JSON.stringify(envelope);
|
|
187
|
+
if (Buffer.byteLength(conteudo, "utf8") > LIMITE_BYTES_CACHE) {
|
|
188
|
+
throw new Error("limite_bytes_cache_incremental_excedido");
|
|
189
|
+
}
|
|
190
|
+
await escreverArquivoWorkspaceSeguro(baseProjeto, caminhoRelativo, conteudo, { sobrescrever: true });
|
|
191
|
+
}
|
|
192
|
+
export async function obterAssinaturaArquivoDrift(arquivo) {
|
|
193
|
+
const info = await lstat(arquivo, { bigint: true });
|
|
194
|
+
if (!info.isFile() || info.isSymbolicLink()) {
|
|
195
|
+
throw new Error(`arquivo_cache_drift_nao_regular:${arquivo}`);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
size: info.size.toString(),
|
|
199
|
+
mtimeNs: info.mtimeNs.toString(),
|
|
200
|
+
ctimeNs: info.ctimeNs.toString(),
|
|
201
|
+
dev: info.dev.toString(),
|
|
202
|
+
ino: info.ino.toString(),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export class CacheIncrementalDrift {
|
|
206
|
+
identidadeDigest;
|
|
207
|
+
politica;
|
|
208
|
+
baseProjeto;
|
|
209
|
+
baseProjetoReal;
|
|
210
|
+
observador;
|
|
211
|
+
caminhoManifestoArquivos;
|
|
212
|
+
caminhoManifestoGrupos;
|
|
213
|
+
manifesto;
|
|
214
|
+
manifestoEmVoo;
|
|
215
|
+
manifestoSujo = false;
|
|
216
|
+
arquivosSujos = new Set();
|
|
217
|
+
manifestoGrupos;
|
|
218
|
+
manifestoGruposEmVoo;
|
|
219
|
+
manifestoGruposSujo = false;
|
|
220
|
+
gruposSujos = new Set();
|
|
221
|
+
arquivosEmVoo = new Map();
|
|
222
|
+
metricasAtuais = {
|
|
223
|
+
metadataHits: 0,
|
|
224
|
+
contentHits: 0,
|
|
225
|
+
fileMisses: 0,
|
|
226
|
+
groupHits: 0,
|
|
227
|
+
groupMisses: 0,
|
|
228
|
+
corruptos: 0,
|
|
229
|
+
extracoesArquivo: 0,
|
|
230
|
+
extracoesGrupo: 0,
|
|
231
|
+
gravacoes: 0,
|
|
232
|
+
errosGravacao: 0,
|
|
233
|
+
};
|
|
234
|
+
constructor(opcoes) {
|
|
235
|
+
this.baseProjeto = path.resolve(opcoes.baseProjeto);
|
|
236
|
+
this.baseProjetoReal = realpath(this.baseProjeto);
|
|
237
|
+
this.politica = opcoes.politica;
|
|
238
|
+
this.identidadeDigest = digestJsonCanonicoCacheDrift(opcoes.identidade);
|
|
239
|
+
this.caminhoManifestoArquivos = caminhoManifestoArquivosDrift(this.identidadeDigest);
|
|
240
|
+
this.caminhoManifestoGrupos = caminhoManifestoGruposDrift(this.identidadeDigest);
|
|
241
|
+
this.observador = opcoes.observador;
|
|
242
|
+
}
|
|
243
|
+
metricas() {
|
|
244
|
+
return { ...this.metricasAtuais };
|
|
245
|
+
}
|
|
246
|
+
async resolverArquivo(opcoes) {
|
|
247
|
+
const chaveExecucao = `${process.platform === "win32" ? path.resolve(opcoes.arquivo).toLowerCase() : path.resolve(opcoes.arquivo)}\0${opcoes.analise}`;
|
|
248
|
+
const existenteEmVoo = this.arquivosEmVoo.get(chaveExecucao);
|
|
249
|
+
if (existenteEmVoo) {
|
|
250
|
+
return existenteEmVoo;
|
|
251
|
+
}
|
|
252
|
+
const execucao = this.resolverArquivoInterno(opcoes);
|
|
253
|
+
this.arquivosEmVoo.set(chaveExecucao, execucao);
|
|
254
|
+
void execucao.catch(() => {
|
|
255
|
+
if (this.arquivosEmVoo.get(chaveExecucao) === execucao) {
|
|
256
|
+
this.arquivosEmVoo.delete(chaveExecucao);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return execucao;
|
|
260
|
+
}
|
|
261
|
+
async resolverArquivoInterno(opcoes) {
|
|
262
|
+
await this.validarArquivoNoWorkspace(opcoes.arquivo);
|
|
263
|
+
const caminho = caminhoRelativoSeguro(this.baseProjeto, opcoes.arquivo);
|
|
264
|
+
const assinar = opcoes.assinar ?? (() => obterAssinaturaArquivoDrift(opcoes.arquivo));
|
|
265
|
+
const assinaturaAntes = await assinar();
|
|
266
|
+
const manifesto = await this.obterManifesto();
|
|
267
|
+
const existente = manifesto.arquivos[caminho];
|
|
268
|
+
const analiseExistente = existente?.analises[opcoes.analise];
|
|
269
|
+
const payloadExistente = analiseExistente?.payload;
|
|
270
|
+
const payloadValido = analiseExistente !== undefined && opcoes.validar(payloadExistente);
|
|
271
|
+
if (analiseExistente && !payloadValido) {
|
|
272
|
+
this.registrarCorrupto(opcoes.arquivo, "payload_schema_invalido");
|
|
273
|
+
}
|
|
274
|
+
if (this.politica === "auto"
|
|
275
|
+
&& existente
|
|
276
|
+
&& payloadValido
|
|
277
|
+
&& assinaturasIguais(existente.assinatura, assinaturaAntes)) {
|
|
278
|
+
this.metricasAtuais.metadataHits += 1;
|
|
279
|
+
emitirEvento(this.observador, {
|
|
280
|
+
tipo: "cache.hit",
|
|
281
|
+
caminho: path.resolve(opcoes.arquivo),
|
|
282
|
+
motivo: "metadata_hit",
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
caminho,
|
|
286
|
+
contentHash: existente.contentHash,
|
|
287
|
+
valor: payloadExistente,
|
|
288
|
+
origem: "metadata_hit",
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
this.metricasAtuais.fileMisses += 1;
|
|
292
|
+
emitirEvento(this.observador, {
|
|
293
|
+
tipo: "cache.miss",
|
|
294
|
+
caminho: path.resolve(opcoes.arquivo),
|
|
295
|
+
motivo: this.politica === "refresh" ? "politica_refresh" : this.politica === "off" ? "politica_off" : "metadata_miss",
|
|
296
|
+
});
|
|
297
|
+
const conteudo = await opcoes.ler();
|
|
298
|
+
if (this.politica === "auto"
|
|
299
|
+
&& existente
|
|
300
|
+
&& payloadValido
|
|
301
|
+
&& existente.contentHash === conteudo.digest) {
|
|
302
|
+
const assinaturaDepois = await assinar();
|
|
303
|
+
if (assinaturasIguais(assinaturaAntes, assinaturaDepois)) {
|
|
304
|
+
existente.assinatura = assinaturaDepois;
|
|
305
|
+
this.manifestoSujo = true;
|
|
306
|
+
this.arquivosSujos.add(caminho);
|
|
307
|
+
}
|
|
308
|
+
this.metricasAtuais.contentHits += 1;
|
|
309
|
+
emitirEvento(this.observador, {
|
|
310
|
+
tipo: "cache.hit",
|
|
311
|
+
caminho: path.resolve(opcoes.arquivo),
|
|
312
|
+
motivo: "content_hit",
|
|
313
|
+
});
|
|
314
|
+
return {
|
|
315
|
+
caminho,
|
|
316
|
+
contentHash: conteudo.digest,
|
|
317
|
+
valor: payloadExistente,
|
|
318
|
+
origem: "content_hit",
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
this.metricasAtuais.extracoesArquivo += 1;
|
|
322
|
+
emitirEvento(this.observador, {
|
|
323
|
+
tipo: "extractor.run",
|
|
324
|
+
caminho: path.resolve(opcoes.arquivo),
|
|
325
|
+
motivo: `arquivo:${opcoes.analise}`,
|
|
326
|
+
});
|
|
327
|
+
const valor = paraJson(await opcoes.extrair(conteudo));
|
|
328
|
+
if (!opcoes.validar(valor)) {
|
|
329
|
+
throw new Error(`extrator_cache_drift_payload_invalido:${opcoes.analise}:${caminho}`);
|
|
330
|
+
}
|
|
331
|
+
if (this.politica !== "off") {
|
|
332
|
+
const assinaturaDepois = await assinar();
|
|
333
|
+
if (assinaturasIguais(assinaturaAntes, assinaturaDepois)) {
|
|
334
|
+
const analises = existente?.contentHash === conteudo.digest
|
|
335
|
+
? { ...existente.analises }
|
|
336
|
+
: {};
|
|
337
|
+
analises[opcoes.analise] = {
|
|
338
|
+
payload: valor,
|
|
339
|
+
payloadDigest: digestJsonCanonicoCacheDrift(valor),
|
|
340
|
+
};
|
|
341
|
+
manifesto.arquivos[caminho] = {
|
|
342
|
+
caminho,
|
|
343
|
+
assinatura: assinaturaDepois,
|
|
344
|
+
contentHash: conteudo.digest,
|
|
345
|
+
analises,
|
|
346
|
+
};
|
|
347
|
+
this.manifestoSujo = true;
|
|
348
|
+
this.arquivosSujos.add(caminho);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
caminho,
|
|
353
|
+
contentHash: conteudo.digest,
|
|
354
|
+
valor,
|
|
355
|
+
origem: "calculado",
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
async resolverGrupo(opcoes) {
|
|
359
|
+
const membros = opcoes.membros
|
|
360
|
+
.map((membro) => ({
|
|
361
|
+
caminho: caminhoRelativoSeguro(this.baseProjeto, membro.caminho),
|
|
362
|
+
contentHash: membro.contentHash,
|
|
363
|
+
}))
|
|
364
|
+
.sort((a, b) => a.caminho.localeCompare(b.caminho, "en"));
|
|
365
|
+
// A chave e ancorada no primeiro arquivo, nao no conteudo nem na composicao
|
|
366
|
+
// atual do grupo. Mudancas sobrescrevem a mesma entrada e evitam crescimento
|
|
367
|
+
// infinito do cache; a lista completa de membros continua validada no envelope.
|
|
368
|
+
const chave = chaveEstavel({ ancora: membros[0]?.caminho ?? "grupo-vazio" });
|
|
369
|
+
const caminhoRelativo = this.caminhoManifestoGrupos;
|
|
370
|
+
if (this.politica === "auto") {
|
|
371
|
+
const manifesto = await this.obterManifestoGrupos();
|
|
372
|
+
const payload = manifesto.grupos[chave];
|
|
373
|
+
if (payload) {
|
|
374
|
+
const identidadeAtual = payload.identidadeDigest === this.identidadeDigest;
|
|
375
|
+
const membrosAtuais = membrosIguais(payload.membros, membros);
|
|
376
|
+
if (identidadeAtual && membrosAtuais) {
|
|
377
|
+
if (opcoes.validar(payload.resumo)) {
|
|
378
|
+
this.metricasAtuais.groupHits += 1;
|
|
379
|
+
emitirEvento(this.observador, {
|
|
380
|
+
tipo: "cache.hit",
|
|
381
|
+
chave,
|
|
382
|
+
caminho: path.resolve(this.baseProjeto, caminhoRelativo),
|
|
383
|
+
motivo: "group_content_hit",
|
|
384
|
+
});
|
|
385
|
+
return { chave, valor: payload.resumo, origem: "cache" };
|
|
386
|
+
}
|
|
387
|
+
this.registrarCorrupto(path.resolve(this.baseProjeto, caminhoRelativo), "payload_schema_invalido");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
this.metricasAtuais.groupMisses += 1;
|
|
392
|
+
emitirEvento(this.observador, {
|
|
393
|
+
tipo: "cache.miss",
|
|
394
|
+
chave,
|
|
395
|
+
caminho: path.resolve(this.baseProjeto, caminhoRelativo),
|
|
396
|
+
motivo: this.politica === "refresh" ? "politica_refresh" : this.politica === "off" ? "politica_off" : "group_miss",
|
|
397
|
+
});
|
|
398
|
+
this.metricasAtuais.extracoesGrupo += 1;
|
|
399
|
+
emitirEvento(this.observador, {
|
|
400
|
+
tipo: "extractor.run",
|
|
401
|
+
chave,
|
|
402
|
+
caminho: opcoes.membros[0]?.caminho,
|
|
403
|
+
motivo: `grupo:${membros.length}`,
|
|
404
|
+
});
|
|
405
|
+
const valor = paraJson(await opcoes.calcular());
|
|
406
|
+
if (!opcoes.validar(valor)) {
|
|
407
|
+
throw new Error(`extrator_grupo_cache_drift_payload_invalido:${chave}`);
|
|
408
|
+
}
|
|
409
|
+
if (this.politica !== "off") {
|
|
410
|
+
const payload = {
|
|
411
|
+
identidadeDigest: this.identidadeDigest,
|
|
412
|
+
membros,
|
|
413
|
+
resumo: valor,
|
|
414
|
+
resumoDigest: digestJsonCanonicoCacheDrift(valor),
|
|
415
|
+
};
|
|
416
|
+
const manifesto = await this.obterManifestoGrupos();
|
|
417
|
+
manifesto.grupos[chave] = payload;
|
|
418
|
+
this.manifestoGruposSujo = true;
|
|
419
|
+
this.gruposSujos.add(chave);
|
|
420
|
+
}
|
|
421
|
+
return { chave, valor, origem: "calculado" };
|
|
422
|
+
}
|
|
423
|
+
async finalizar() {
|
|
424
|
+
if (this.politica === "off") {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (this.manifestoSujo && this.manifesto) {
|
|
428
|
+
try {
|
|
429
|
+
this.manifesto = await this.gravarManifestoArquivosComMerge(this.manifesto);
|
|
430
|
+
this.metricasAtuais.gravacoes += 1;
|
|
431
|
+
this.manifestoSujo = false;
|
|
432
|
+
this.arquivosSujos.clear();
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
this.metricasAtuais.errosGravacao += 1;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (this.manifestoGruposSujo && this.manifestoGrupos) {
|
|
439
|
+
try {
|
|
440
|
+
this.manifestoGrupos = await this.gravarManifestoGruposComMerge(this.manifestoGrupos);
|
|
441
|
+
this.metricasAtuais.gravacoes += 1;
|
|
442
|
+
this.manifestoGruposSujo = false;
|
|
443
|
+
this.gruposSujos.clear();
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
this.metricasAtuais.errosGravacao += 1;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
chaveEnvelope(tipo) {
|
|
451
|
+
return `${tipo}:${this.identidadeDigest}`;
|
|
452
|
+
}
|
|
453
|
+
async lerManifestoArquivosAtual() {
|
|
454
|
+
const leitura = await lerEnvelope(this.baseProjeto, this.caminhoManifestoArquivos, SCHEMA_MANIFESTO_ARQUIVOS_DRIFT, this.chaveEnvelope("files"));
|
|
455
|
+
return leitura.estado === "hit" && manifestoValido(leitura.payload, this.identidadeDigest)
|
|
456
|
+
? leitura.payload
|
|
457
|
+
: undefined;
|
|
458
|
+
}
|
|
459
|
+
async lerManifestoGruposAtual() {
|
|
460
|
+
const leitura = await lerEnvelope(this.baseProjeto, this.caminhoManifestoGrupos, SCHEMA_GRUPO_INDICE_DRIFT, this.chaveEnvelope("groups"));
|
|
461
|
+
return leitura.estado === "hit" && manifestoGruposValido(leitura.payload, this.identidadeDigest)
|
|
462
|
+
? leitura.payload
|
|
463
|
+
: undefined;
|
|
464
|
+
}
|
|
465
|
+
async gravarManifestoArquivosComMerge(local) {
|
|
466
|
+
let ultimoErro;
|
|
467
|
+
for (let tentativa = 0; tentativa < 2; tentativa += 1) {
|
|
468
|
+
const publicado = await this.lerManifestoArquivosAtual();
|
|
469
|
+
const mesclado = {
|
|
470
|
+
identidadeDigest: this.identidadeDigest,
|
|
471
|
+
arquivos: publicado ? { ...publicado.arquivos } : {},
|
|
472
|
+
};
|
|
473
|
+
for (const caminho of this.arquivosSujos) {
|
|
474
|
+
const entrada = local.arquivos[caminho];
|
|
475
|
+
if (entrada) {
|
|
476
|
+
mesclado.arquivos[caminho] = entrada;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
await escreverEnvelope(this.baseProjeto, this.caminhoManifestoArquivos, SCHEMA_MANIFESTO_ARQUIVOS_DRIFT, this.chaveEnvelope("files"), mesclado);
|
|
481
|
+
return mesclado;
|
|
482
|
+
}
|
|
483
|
+
catch (erro) {
|
|
484
|
+
ultimoErro = erro;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
throw ultimoErro;
|
|
488
|
+
}
|
|
489
|
+
async gravarManifestoGruposComMerge(local) {
|
|
490
|
+
let ultimoErro;
|
|
491
|
+
for (let tentativa = 0; tentativa < 2; tentativa += 1) {
|
|
492
|
+
const publicado = await this.lerManifestoGruposAtual();
|
|
493
|
+
const mesclado = {
|
|
494
|
+
identidadeDigest: this.identidadeDigest,
|
|
495
|
+
grupos: publicado ? { ...publicado.grupos } : {},
|
|
496
|
+
};
|
|
497
|
+
for (const chave of this.gruposSujos) {
|
|
498
|
+
const entrada = local.grupos[chave];
|
|
499
|
+
if (entrada) {
|
|
500
|
+
mesclado.grupos[chave] = entrada;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
try {
|
|
504
|
+
await escreverEnvelope(this.baseProjeto, this.caminhoManifestoGrupos, SCHEMA_GRUPO_INDICE_DRIFT, this.chaveEnvelope("groups"), mesclado);
|
|
505
|
+
return mesclado;
|
|
506
|
+
}
|
|
507
|
+
catch (erro) {
|
|
508
|
+
ultimoErro = erro;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
throw ultimoErro;
|
|
512
|
+
}
|
|
513
|
+
async obterManifesto() {
|
|
514
|
+
if (this.manifesto) {
|
|
515
|
+
return this.manifesto;
|
|
516
|
+
}
|
|
517
|
+
if (this.manifestoEmVoo) {
|
|
518
|
+
return this.manifestoEmVoo;
|
|
519
|
+
}
|
|
520
|
+
this.manifestoEmVoo = this.carregarManifesto();
|
|
521
|
+
try {
|
|
522
|
+
return await this.manifestoEmVoo;
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
this.manifestoEmVoo = undefined;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async carregarManifesto() {
|
|
529
|
+
if (this.politica === "auto") {
|
|
530
|
+
const leitura = await lerEnvelope(this.baseProjeto, this.caminhoManifestoArquivos, SCHEMA_MANIFESTO_ARQUIVOS_DRIFT, this.chaveEnvelope("files"));
|
|
531
|
+
if (leitura.estado === "hit" && manifestoValido(leitura.payload, this.identidadeDigest)) {
|
|
532
|
+
this.manifesto = leitura.payload;
|
|
533
|
+
return this.manifesto;
|
|
534
|
+
}
|
|
535
|
+
if (leitura.estado === "corrupt" || leitura.estado === "hit") {
|
|
536
|
+
this.registrarCorrupto(path.resolve(this.baseProjeto, this.caminhoManifestoArquivos), leitura.estado === "hit" ? "payload_schema_invalido" : leitura.motivo);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
this.manifesto = {
|
|
540
|
+
identidadeDigest: this.identidadeDigest,
|
|
541
|
+
arquivos: {},
|
|
542
|
+
};
|
|
543
|
+
return this.manifesto;
|
|
544
|
+
}
|
|
545
|
+
async obterManifestoGrupos() {
|
|
546
|
+
if (this.manifestoGrupos) {
|
|
547
|
+
return this.manifestoGrupos;
|
|
548
|
+
}
|
|
549
|
+
if (this.manifestoGruposEmVoo) {
|
|
550
|
+
return this.manifestoGruposEmVoo;
|
|
551
|
+
}
|
|
552
|
+
this.manifestoGruposEmVoo = this.carregarManifestoGrupos();
|
|
553
|
+
try {
|
|
554
|
+
return await this.manifestoGruposEmVoo;
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
this.manifestoGruposEmVoo = undefined;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async carregarManifestoGrupos() {
|
|
561
|
+
if (this.politica === "auto") {
|
|
562
|
+
const leitura = await lerEnvelope(this.baseProjeto, this.caminhoManifestoGrupos, SCHEMA_GRUPO_INDICE_DRIFT, this.chaveEnvelope("groups"));
|
|
563
|
+
if (leitura.estado === "hit" && manifestoGruposValido(leitura.payload, this.identidadeDigest)) {
|
|
564
|
+
this.manifestoGrupos = leitura.payload;
|
|
565
|
+
return this.manifestoGrupos;
|
|
566
|
+
}
|
|
567
|
+
if (leitura.estado === "corrupt" || leitura.estado === "hit") {
|
|
568
|
+
this.registrarCorrupto(path.resolve(this.baseProjeto, this.caminhoManifestoGrupos), leitura.estado === "hit" ? "payload_schema_invalido" : leitura.motivo);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
this.manifestoGrupos = {
|
|
572
|
+
identidadeDigest: this.identidadeDigest,
|
|
573
|
+
grupos: {},
|
|
574
|
+
};
|
|
575
|
+
return this.manifestoGrupos;
|
|
576
|
+
}
|
|
577
|
+
registrarCorrupto(caminho, motivo) {
|
|
578
|
+
this.metricasAtuais.corruptos += 1;
|
|
579
|
+
emitirEvento(this.observador, {
|
|
580
|
+
tipo: "cache.corrupt",
|
|
581
|
+
caminho,
|
|
582
|
+
motivo,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
async validarArquivoNoWorkspace(arquivo) {
|
|
586
|
+
const [baseReal, arquivoReal] = await Promise.all([
|
|
587
|
+
this.baseProjetoReal,
|
|
588
|
+
realpath(arquivo),
|
|
589
|
+
]);
|
|
590
|
+
const relativo = path.relative(baseReal, arquivoReal);
|
|
591
|
+
if (relativo === ".." || relativo.startsWith(`..${path.sep}`) || path.isAbsolute(relativo)) {
|
|
592
|
+
throw new Error(`arquivo_cache_drift_fora_workspace_real:${arquivo}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
//# sourceMappingURL=driftCacheIncremental.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const SCHEMA_SNAPSHOT_CATALOGO_DRIFT: "sema.drift-catalog/v1";
|
|
2
|
+
export type TipoEventoOperacaoDrift = "catalog.visit" | "content.open" | "content.read" | "ast.create" | "extractor.run" | "cache.hit" | "cache.miss" | "cache.corrupt";
|
|
3
|
+
export interface EventoOperacaoDrift {
|
|
4
|
+
tipo: TipoEventoOperacaoDrift;
|
|
5
|
+
caminho?: string;
|
|
6
|
+
raiz?: string;
|
|
7
|
+
chave?: string;
|
|
8
|
+
categoria?: "arquivo" | "diretorio";
|
|
9
|
+
bytes?: number;
|
|
10
|
+
digest?: string;
|
|
11
|
+
motivo?: string;
|
|
12
|
+
}
|
|
13
|
+
export type ObservadorOperacaoDrift = (evento: EventoOperacaoDrift) => void;
|
|
14
|
+
export type ObservadorDrift = ObservadorOperacaoDrift;
|
|
15
|
+
export interface ArquivoCatalogadoDrift {
|
|
16
|
+
caminho: string;
|
|
17
|
+
extensao: string;
|
|
18
|
+
explicito: boolean;
|
|
19
|
+
raizes: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface ConteudoArquivoDrift {
|
|
22
|
+
caminho: string;
|
|
23
|
+
bytes: Buffer;
|
|
24
|
+
texto: string;
|
|
25
|
+
digest: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MetricasCatalogoDrift {
|
|
28
|
+
diretoriosVisitados: number;
|
|
29
|
+
arquivosCatalogados: number;
|
|
30
|
+
leiturasConteudo: number;
|
|
31
|
+
bytesLidos: number;
|
|
32
|
+
acertosMemoriaConteudo: number;
|
|
33
|
+
origem: "plano_explicito" | "caminhada" | "snapshot";
|
|
34
|
+
diretoriosValidadosCache: number;
|
|
35
|
+
}
|
|
36
|
+
export interface AssinaturaDiretorioCatalogoDrift {
|
|
37
|
+
mtimeNs: string;
|
|
38
|
+
ctimeNs: string;
|
|
39
|
+
dev: string;
|
|
40
|
+
ino: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SnapshotCatalogoDrift {
|
|
43
|
+
schema: typeof SCHEMA_SNAPSHOT_CATALOGO_DRIFT;
|
|
44
|
+
raizes: string[];
|
|
45
|
+
extensoes: string[] | null;
|
|
46
|
+
diretoriosIgnorados: string[];
|
|
47
|
+
diretorios: Array<{
|
|
48
|
+
caminho: string;
|
|
49
|
+
assinatura: AssinaturaDiretorioCatalogoDrift;
|
|
50
|
+
}>;
|
|
51
|
+
arquivos: Array<{
|
|
52
|
+
caminho: string;
|
|
53
|
+
explicito: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
export interface FiltroCatalogoDrift {
|
|
57
|
+
extensoes?: readonly string[];
|
|
58
|
+
raiz?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface OpcoesCatalogoDrift {
|
|
61
|
+
baseDiretorio?: string;
|
|
62
|
+
arquivos?: readonly string[];
|
|
63
|
+
raizes?: readonly string[];
|
|
64
|
+
extensoes?: readonly string[];
|
|
65
|
+
diretoriosIgnorados?: ReadonlySet<string> | readonly string[];
|
|
66
|
+
concorrencia?: number;
|
|
67
|
+
observador?: ObservadorOperacaoDrift;
|
|
68
|
+
snapshot?: SnapshotCatalogoDrift;
|
|
69
|
+
}
|
|
70
|
+
export interface CatalogoDrift {
|
|
71
|
+
readonly baseDiretorio: string;
|
|
72
|
+
readonly raizes: readonly string[];
|
|
73
|
+
listar(filtro?: FiltroCatalogoDrift): string[];
|
|
74
|
+
listar(extensoes?: readonly string[], raizes?: readonly string[]): string[];
|
|
75
|
+
listarEntradas(filtro?: FiltroCatalogoDrift): ArquivoCatalogadoDrift[];
|
|
76
|
+
listarPorExtensoes(extensoes: readonly string[], raiz?: string): string[];
|
|
77
|
+
listarPorRaiz(raiz: string, extensoes?: readonly string[]): string[];
|
|
78
|
+
arquivosCatalogados(): string[];
|
|
79
|
+
incluir(arquivos: readonly string[]): Promise<void>;
|
|
80
|
+
contem(caminho: string): boolean;
|
|
81
|
+
ler(caminho: string): Promise<ConteudoArquivoDrift>;
|
|
82
|
+
lerTexto(caminho: string): Promise<string>;
|
|
83
|
+
digest(caminho: string): Promise<string>;
|
|
84
|
+
fingerprint(caminhos?: readonly string[]): Promise<string>;
|
|
85
|
+
obterFingerprint(caminhos?: readonly string[]): Promise<string>;
|
|
86
|
+
emitir(tipo: TipoEventoOperacaoDrift, arquivo?: string): void;
|
|
87
|
+
metricas(): MetricasCatalogoDrift;
|
|
88
|
+
snapshot(): SnapshotCatalogoDrift;
|
|
89
|
+
}
|
|
90
|
+
export type CatalogoArquivosDrift = CatalogoDrift;
|
|
91
|
+
export declare function criarCatalogoDrift(opcoes: OpcoesCatalogoDrift): Promise<CatalogoDrift>;
|