@semacode/cli 2.3.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.
Files changed (177) hide show
  1. package/LICENSE +39 -39
  2. package/README.md +251 -228
  3. package/dist/agentEntryPoints.js +551 -550
  4. package/dist/angular-consumer-standalone.d.ts +3 -1
  5. package/dist/angular-consumer-standalone.js +12 -8
  6. package/dist/drift.part01.d.ts +32 -2
  7. package/dist/drift.part01.js +2 -0
  8. package/dist/drift.part04.d.ts +3 -2
  9. package/dist/drift.part04.js +6 -3
  10. package/dist/drift.part05.d.ts +2 -1
  11. package/dist/drift.part05.js +4 -3
  12. package/dist/drift.part06.d.ts +2 -1
  13. package/dist/drift.part06.js +9 -5
  14. package/dist/drift.part07.d.ts +11 -10
  15. package/dist/drift.part07.js +40 -30
  16. package/dist/drift.part08.d.ts +2 -1
  17. package/dist/drift.part08.js +4 -3
  18. package/dist/drift.part11.js +71 -6
  19. package/dist/drift.part12.js +14 -1
  20. package/dist/drift.part13.d.ts +42 -7
  21. package/dist/drift.part13.js +660 -36
  22. package/dist/driftCache.d.ts +49 -0
  23. package/dist/driftCache.js +311 -0
  24. package/dist/driftCacheIncremental.d.ts +98 -0
  25. package/dist/driftCacheIncremental.js +596 -0
  26. package/dist/driftCatalogo.d.ts +91 -0
  27. package/dist/driftCatalogo.js +657 -0
  28. package/dist/driftCatalogoPersistente.d.ts +8 -0
  29. package/dist/driftCatalogoPersistente.js +77 -0
  30. package/dist/driftEscopo.d.ts +23 -0
  31. package/dist/driftEscopo.js +563 -0
  32. package/dist/driftEscopoReferencias.d.ts +4 -0
  33. package/dist/driftEscopoReferencias.js +265 -0
  34. package/dist/driftOrcamento.d.ts +12 -0
  35. package/dist/driftOrcamento.js +19 -3
  36. package/dist/execucoesExternas.js +5 -19
  37. package/dist/iaPrompts.js +310 -310
  38. package/dist/index.part01.d.ts +16 -1
  39. package/dist/index.part01.js +80 -4
  40. package/dist/index.part02.js +21 -21
  41. package/dist/index.part03.d.ts +2 -2
  42. package/dist/index.part03.js +14 -3
  43. package/dist/index.part04.d.ts +2 -2
  44. package/dist/index.part04.js +113 -101
  45. package/dist/index.part05.d.ts +1 -1
  46. package/dist/index.part05.js +99 -22
  47. package/dist/index.part06.js +3 -2
  48. package/dist/index.part07.js +24 -24
  49. package/dist/index.part08.js +1 -1
  50. package/dist/init/templates.part01.js +393 -393
  51. package/dist/init/templates.part02.js +189 -189
  52. package/dist/initTemplatesBackend.js +73 -73
  53. package/dist/initTemplatesBase.js +81 -81
  54. package/dist/initTemplatesFlutter.js +137 -137
  55. package/dist/initTemplatesRuntime.js +350 -350
  56. package/dist/initTemplatesWeb.js +309 -309
  57. package/dist/pipelineConteudo/command.js +1 -36
  58. package/dist/projeto.d.ts +1 -1
  59. package/dist/projetoCarregar.d.ts +2 -2
  60. package/dist/projetoCarregar.js +47 -24
  61. package/dist/projetoCatalogo.d.ts +7 -0
  62. package/dist/projetoCatalogo.js +125 -0
  63. package/dist/projetoTipos.d.ts +14 -5
  64. package/dist/sync/index.js +35 -35
  65. package/docs/README.md +34 -34
  66. package/docs/ai-integration.md +1 -7
  67. package/docs/ai-onboarding.md +62 -62
  68. package/docs/ai-workflow.md +4 -0
  69. package/docs/auth.md +26 -26
  70. package/docs/cli.md +79 -79
  71. package/docs/commands.md +16 -13
  72. package/docs/database.md +15 -15
  73. package/docs/deploy.md +41 -41
  74. package/docs/descoberta-capacidades.md +106 -106
  75. package/docs/documentation.md +37 -46
  76. package/docs/env.md +16 -16
  77. package/docs/frontend.md +41 -41
  78. package/docs/getting-started.md +57 -57
  79. package/docs/i18n.md +15 -15
  80. package/docs/pipeline-conteudo.md +246 -246
  81. package/docs/profiles.md +54 -54
  82. package/docs/repositories.md +43 -43
  83. package/docs/rollback.md +31 -31
  84. package/docs/security.md +32 -32
  85. package/docs/sistemas-interativos.md +373 -378
  86. package/docs/support.md +8 -8
  87. package/docs/testing.md +51 -44
  88. package/docs/vocabulary.md +29 -29
  89. package/exemplos/agendamento.sema +1 -1
  90. package/exemplos/assinatura.sema +1 -1
  91. package/exemplos/auditoria.sema +1 -1
  92. package/exemplos/autenticacao.sema +1 -1
  93. package/exemplos/author_obra_comum.sema +1 -1
  94. package/exemplos/author_tema_sensivel.sema +1 -1
  95. package/exemplos/automacao.sema +1 -1
  96. package/exemplos/cadastro_usuario.sema +1 -1
  97. package/exemplos/calculadora.sema +1 -1
  98. package/exemplos/crud_simples.sema +1 -1
  99. package/exemplos/estoque.sema +1 -1
  100. package/exemplos/exportacao.sema +1 -1
  101. package/exemplos/fila.sema +1 -1
  102. package/exemplos/integracao_externa.sema +1 -1
  103. package/exemplos/multi_tenant.sema +1 -1
  104. package/exemplos/notificacao.sema +1 -1
  105. package/exemplos/operacao_estrategia.sema +132 -132
  106. package/exemplos/operacao_estrategia_discovery.sema +220 -220
  107. package/exemplos/operacao_estrategia_fluxo.sema +114 -114
  108. package/exemplos/operacao_estrategia_gates.sema +106 -106
  109. package/exemplos/operacao_estrategia_quarentena.sema +133 -133
  110. package/exemplos/operacao_estrategia_rollout.sema +124 -124
  111. package/exemplos/pagamento.sema +1 -1
  112. package/exemplos/pagamento_dominio.sema +1 -1
  113. package/exemplos/pedido.sema +1 -1
  114. package/exemplos/permissao.sema +1 -1
  115. package/exemplos/persistencia_vendor_first.sema +1 -1
  116. package/exemplos/pipeline-conteudo/README.md +44 -44
  117. package/exemplos/pipeline-conteudo/alvos.json +23 -23
  118. package/exemplos/pipeline-conteudo/definicao.json +136 -136
  119. package/exemplos/profile_conversas.sema +165 -165
  120. package/exemplos/profile_game.sema +1 -1
  121. package/exemplos/profile_legal.sema +1 -1
  122. package/exemplos/profile_ops.sema +1 -1
  123. package/exemplos/profile_research.sema +1 -1
  124. package/exemplos/profile_simulation.sema +211 -211
  125. package/exemplos/profile_software.sema +1 -1
  126. package/exemplos/profile_workflow_n8n.sema +1 -1
  127. package/exemplos/relatorio.sema +1 -1
  128. package/exemplos/replica_analitica_erp.sema +1 -1
  129. package/exemplos/sistemas-interativos/README.md +100 -100
  130. package/exemplos/sistemas-interativos/advanced/acceptance-context-evaluate-valid.json +5 -5
  131. package/exemplos/sistemas-interativos/advanced/acceptance-lock-valid.json +16 -16
  132. package/exemplos/sistemas-interativos/advanced/asset-provenance-valid.json +32 -32
  133. package/exemplos/sistemas-interativos/advanced/autonomy-repair-valid.json +93 -93
  134. package/exemplos/sistemas-interativos/advanced/control-run-definition-valid.json +78 -78
  135. package/exemplos/sistemas-interativos/advanced/control-run-valid.json +27 -27
  136. package/exemplos/sistemas-interativos/advanced/distributed-workers-valid.json +79 -79
  137. package/exemplos/sistemas-interativos/advanced/editor-state-valid.json +42 -42
  138. package/exemplos/sistemas-interativos/advanced/engine-snapshot-after-valid.json +32 -32
  139. package/exemplos/sistemas-interativos/advanced/engine-snapshot-before-valid.json +23 -23
  140. package/exemplos/sistemas-interativos/advanced/job-orchestration-valid.json +39 -39
  141. package/exemplos/sistemas-interativos/advanced/multimodal-evidence-valid.json +46 -46
  142. package/exemplos/sistemas-interativos/advanced/multiplayer-authority-valid.json +39 -39
  143. package/exemplos/sistemas-interativos/advanced/playtest-fuzz-valid.json +37 -37
  144. package/exemplos/sistemas-interativos/advanced/portability-valid.json +104 -104
  145. package/exemplos/sistemas-interativos/advanced/temporal-evidence-valid.json +27 -27
  146. package/exemplos/sistemas-interativos/advanced/temporal-valid.json +118 -118
  147. package/exemplos/sistemas-interativos/experience-ir-valid.json +459 -459
  148. package/exemplos/sistemas-interativos/game-2d.json +83 -83
  149. package/exemplos/sistemas-interativos/game-3d-human.json +81 -81
  150. package/exemplos/sistemas-interativos/game-pixel-16-bit.json +97 -97
  151. package/exemplos/sistemas-interativos/game-pixel-8-bit.json +94 -94
  152. package/exemplos/sistemas-interativos/game-xr-human.json +80 -80
  153. package/exemplos/sistemas-interativos/hybrid-2-5d.json +108 -108
  154. package/exemplos/sistemas-interativos/protocol-mutating-rollback-valid.json +75 -75
  155. package/exemplos/sistemas-interativos/protocol-read-only-valid.json +58 -58
  156. package/exemplos/sistemas-interativos/simulation-2d-controlled.json +97 -97
  157. package/exemplos/sistemas-interativos/simulation-3d-calibrated-autonomous.json +141 -141
  158. package/exemplos/sistemas-interativos/simulation-headless-autonomous-batch.json +112 -112
  159. package/exemplos/sistemas-interativos/simulation-pixel-16-bit.json +103 -103
  160. package/exemplos/sistemas-interativos/simulation-text-controlled.json +95 -95
  161. package/exemplos/testes_embutidos.sema +1 -1
  162. package/exemplos/tratamento_erro.sema +1 -1
  163. package/exemplos/upload_arquivo.sema +1 -1
  164. package/exemplos/webhook.sema +1 -1
  165. package/node_modules/@sema/gerador-cpp/dist/index.js +176 -176
  166. package/node_modules/@sema/gerador-cpp/package.json +1 -1
  167. package/node_modules/@sema/gerador-css/dist/index.part01.js +10 -10
  168. package/node_modules/@sema/gerador-css/dist/index.part02.js +460 -460
  169. package/node_modules/@sema/gerador-dotnet/dist/index.js +169 -169
  170. package/node_modules/@sema/gerador-dotnet/package.json +1 -1
  171. package/node_modules/@sema/gerador-html/dist/index.js +17 -17
  172. package/node_modules/@sema/gerador-lua/dist/index.js +51 -51
  173. package/node_modules/@sema/gerador-php/dist/index.js +113 -113
  174. package/node_modules/@sema/gerador-python/dist/index.part02.js +3 -3
  175. package/node_modules/@sema/gerador-typescript/dist/index.part02.js +11 -11
  176. package/node_modules/@sema/nucleo/dist/diagnosticos/melhorador.js +42 -42
  177. package/package.json +3 -3
@@ -0,0 +1,657 @@
1
+ // SEMA-GOVERNED: sema.produto.governanca_ia.drift
2
+ // Descrição: cataloga somente o plano explícito do drift e compartilha leituras e fingerprints de conteúdo.
3
+ import { createHash } from "node:crypto";
4
+ import { lstat, open, readdir, realpath } from "node:fs/promises";
5
+ import path from "node:path";
6
+ export const SCHEMA_SNAPSHOT_CATALOGO_DRIFT = "sema.drift-catalog/v1";
7
+ const CONCORRENCIA_PADRAO = 8;
8
+ const CONCORRENCIA_MAXIMA = 64;
9
+ function emitirEvento(observador, evento) {
10
+ try {
11
+ observador?.(evento);
12
+ }
13
+ catch {
14
+ // Observabilidade nunca altera o resultado funcional do drift.
15
+ }
16
+ }
17
+ function normalizarConcorrencia(valor) {
18
+ if (!Number.isFinite(valor)) {
19
+ return CONCORRENCIA_PADRAO;
20
+ }
21
+ return Math.max(1, Math.min(CONCORRENCIA_MAXIMA, Math.trunc(valor)));
22
+ }
23
+ function normalizarExtensao(extensao) {
24
+ const limpa = extensao.trim().toLowerCase();
25
+ if (!limpa) {
26
+ return limpa;
27
+ }
28
+ return limpa.startsWith(".") ? limpa : `.${limpa}`;
29
+ }
30
+ function ehFiltroCatalogoDrift(valor) {
31
+ return !Array.isArray(valor);
32
+ }
33
+ function chaveCaminho(caminho) {
34
+ const normalizado = path.normalize(path.resolve(caminho));
35
+ return process.platform === "win32" ? normalizado.toLowerCase() : normalizado;
36
+ }
37
+ function caminhoEstaDentro(raiz, alvo) {
38
+ const relativo = path.relative(raiz, alvo);
39
+ return relativo === "" || (relativo !== ".."
40
+ && !relativo.startsWith(`..${path.sep}`)
41
+ && !path.isAbsolute(relativo));
42
+ }
43
+ function erroEhAusencia(erro) {
44
+ return ["ENOENT", "ENOTDIR"].includes(erro.code ?? "");
45
+ }
46
+ function assinaturaDiretorio(info) {
47
+ return {
48
+ mtimeNs: info.mtimeNs.toString(),
49
+ ctimeNs: info.ctimeNs.toString(),
50
+ dev: info.dev.toString(),
51
+ ino: info.ino.toString(),
52
+ };
53
+ }
54
+ function assinaturasDiretorioIguais(a, b) {
55
+ return a.mtimeNs === b.mtimeNs
56
+ && a.ctimeNs === b.ctimeNs
57
+ && a.dev === b.dev
58
+ && a.ino === b.ino;
59
+ }
60
+ function identidadeArquivoEstavel(a, b) {
61
+ return a.isFile()
62
+ && b.isFile()
63
+ && !a.isSymbolicLink()
64
+ && !b.isSymbolicLink()
65
+ && a.dev === b.dev
66
+ && a.ino === b.ino
67
+ && a.size === b.size
68
+ && a.mtimeNs === b.mtimeNs
69
+ && a.ctimeNs === b.ctimeNs;
70
+ }
71
+ function registroObjeto(valor) {
72
+ return typeof valor === "object" && valor !== null && !Array.isArray(valor);
73
+ }
74
+ function caminhoRelativoSeguro(valor) {
75
+ return typeof valor === "string"
76
+ && valor.length > 0
77
+ && valor !== "."
78
+ && valor !== ".."
79
+ && !valor.startsWith("../")
80
+ && !valor.includes("\\")
81
+ && !path.posix.isAbsolute(valor);
82
+ }
83
+ function diretorioRelativoSeguro(valor) {
84
+ return valor === "." || caminhoRelativoSeguro(valor);
85
+ }
86
+ function assinaturaDiretorioValida(valor) {
87
+ return registroObjeto(valor)
88
+ && ["mtimeNs", "ctimeNs", "dev", "ino"]
89
+ .every((campo) => typeof valor[campo] === "string" && /^\d+$/u.test(valor[campo]));
90
+ }
91
+ async function mapearComLimite(itens, limite, executar) {
92
+ if (itens.length === 0) {
93
+ return [];
94
+ }
95
+ const resultados = new Array(itens.length);
96
+ let proximoIndice = 0;
97
+ const trabalhador = async () => {
98
+ for (;;) {
99
+ const indice = proximoIndice;
100
+ proximoIndice += 1;
101
+ if (indice >= itens.length) {
102
+ return;
103
+ }
104
+ resultados[indice] = await executar(itens[indice]);
105
+ }
106
+ };
107
+ await Promise.all(Array.from({ length: Math.min(limite, itens.length) }, () => trabalhador()));
108
+ return resultados;
109
+ }
110
+ class CatalogoDriftImplementacao {
111
+ baseDiretorio;
112
+ raizes;
113
+ registros = new Map();
114
+ conteudos = new Map();
115
+ extensoesPermitidas;
116
+ concorrencia;
117
+ observador;
118
+ baseDiretorioReal;
119
+ extensoesSnapshot;
120
+ diretoriosVisitados = new Map();
121
+ diretoriosIgnoradosSnapshot = [];
122
+ contadores = {
123
+ diretoriosVisitados: 0,
124
+ arquivosCatalogados: 0,
125
+ leiturasConteudo: 0,
126
+ bytesLidos: 0,
127
+ acertosMemoriaConteudo: 0,
128
+ origem: "plano_explicito",
129
+ diretoriosValidadosCache: 0,
130
+ };
131
+ constructor(opcoes) {
132
+ this.baseDiretorio = path.resolve(opcoes.baseDiretorio ?? process.cwd());
133
+ this.baseDiretorioReal = realpath(this.baseDiretorio);
134
+ this.raizes = [...new Map((opcoes.raizes ?? [])
135
+ .map((raiz) => path.resolve(this.baseDiretorio, raiz))
136
+ .map((raiz) => [chaveCaminho(raiz), raiz])).values()].sort((a, b) => a.localeCompare(b, "pt-BR"));
137
+ const extensoes = opcoes.extensoes
138
+ ?.map(normalizarExtensao)
139
+ .filter(Boolean);
140
+ this.extensoesPermitidas = extensoes?.length
141
+ ? new Set(extensoes)
142
+ : undefined;
143
+ this.extensoesSnapshot = this.extensoesPermitidas
144
+ ? [...this.extensoesPermitidas].sort((a, b) => a.localeCompare(b, "en"))
145
+ : null;
146
+ this.concorrencia = normalizarConcorrencia(opcoes.concorrencia);
147
+ this.observador = opcoes.observador;
148
+ }
149
+ async construir(arquivosExplicitos, diretoriosIgnorados, snapshot) {
150
+ await this.baseDiretorioReal;
151
+ this.diretoriosIgnoradosSnapshot = [...diretoriosIgnorados]
152
+ .sort((a, b) => a.localeCompare(b, "en"));
153
+ const restaurado = snapshot
154
+ ? await this.restaurarSnapshot(snapshot, diretoriosIgnorados, arquivosExplicitos)
155
+ : false;
156
+ if (!restaurado) {
157
+ this.contadores.origem = this.raizes.length > 0 ? "caminhada" : "plano_explicito";
158
+ await this.caminharRaizes(diretoriosIgnorados);
159
+ }
160
+ await this.incluir(arquivosExplicitos);
161
+ }
162
+ async incluir(arquivosExplicitos) {
163
+ const explicitos = [...new Map(arquivosExplicitos
164
+ .map((arquivo) => path.resolve(this.baseDiretorio, arquivo))
165
+ .map((arquivo) => [chaveCaminho(arquivo), arquivo])).values()].sort((a, b) => a.localeCompare(b, "pt-BR"));
166
+ await mapearComLimite(explicitos, this.concorrencia, async (arquivo) => {
167
+ if (!await this.caminhoPermaneceNoWorkspace(arquivo)) {
168
+ return;
169
+ }
170
+ let informacao;
171
+ try {
172
+ informacao = await lstat(arquivo);
173
+ }
174
+ catch (erro) {
175
+ if (erroEhAusencia(erro)) {
176
+ return;
177
+ }
178
+ throw erro;
179
+ }
180
+ if (!informacao.isFile() || informacao.isSymbolicLink()) {
181
+ return;
182
+ }
183
+ this.adicionarArquivo(arquivo, true);
184
+ });
185
+ }
186
+ listar(extensoesOuFiltro = [], raizes) {
187
+ if (ehFiltroCatalogoDrift(extensoesOuFiltro)) {
188
+ return this.listarEntradas(extensoesOuFiltro).map((arquivo) => arquivo.caminho);
189
+ }
190
+ const extensoes = extensoesOuFiltro.length ? extensoesOuFiltro : undefined;
191
+ if (!raizes?.length) {
192
+ return this.listarEntradas({ extensoes }).map((arquivo) => arquivo.caminho);
193
+ }
194
+ const raizesAbsolutas = raizes.map((raiz) => path.resolve(this.baseDiretorio, raiz));
195
+ return this.listarEntradas({ extensoes })
196
+ .filter((arquivo) => raizesAbsolutas.some((raiz) => caminhoEstaDentro(raiz, arquivo.caminho)))
197
+ .map((arquivo) => arquivo.caminho);
198
+ }
199
+ listarEntradas(filtro = {}) {
200
+ const extensoes = filtro.extensoes
201
+ ? new Set(filtro.extensoes.map(normalizarExtensao).filter(Boolean))
202
+ : undefined;
203
+ const raiz = filtro.raiz
204
+ ? path.resolve(this.baseDiretorio, filtro.raiz)
205
+ : undefined;
206
+ return [...this.registros.values()]
207
+ .filter((registro) => !extensoes || extensoes.has(registro.extensao))
208
+ .filter((registro) => !raiz || caminhoEstaDentro(raiz, registro.caminho))
209
+ .map((registro) => ({
210
+ caminho: registro.caminho,
211
+ extensao: registro.extensao,
212
+ explicito: registro.explicito,
213
+ raizes: [...registro.raizes].sort((a, b) => a.localeCompare(b, "pt-BR")),
214
+ }))
215
+ .sort((a, b) => a.caminho.localeCompare(b.caminho, "pt-BR"));
216
+ }
217
+ listarPorExtensoes(extensoes, raiz) {
218
+ return raiz ? this.listar(extensoes, [raiz]) : this.listar(extensoes);
219
+ }
220
+ listarPorRaiz(raiz, extensoes) {
221
+ return this.listar(extensoes, [raiz]);
222
+ }
223
+ arquivosCatalogados() {
224
+ return this.listar();
225
+ }
226
+ contem(caminho) {
227
+ return this.registros.has(chaveCaminho(path.resolve(this.baseDiretorio, caminho)));
228
+ }
229
+ ler(caminho) {
230
+ const absoluto = path.resolve(this.baseDiretorio, caminho);
231
+ const chave = chaveCaminho(absoluto);
232
+ const registro = this.registros.get(chave);
233
+ if (!registro) {
234
+ return Promise.reject(new Error(`Arquivo fora do plano explicito do drift: ${absoluto}`));
235
+ }
236
+ const existente = this.conteudos.get(chave);
237
+ if (existente) {
238
+ this.contadores.acertosMemoriaConteudo += 1;
239
+ return existente;
240
+ }
241
+ const leitura = this.lerFisicamente(registro.caminho);
242
+ this.conteudos.set(chave, leitura);
243
+ void leitura.catch(() => {
244
+ if (this.conteudos.get(chave) === leitura) {
245
+ this.conteudos.delete(chave);
246
+ }
247
+ });
248
+ return leitura;
249
+ }
250
+ async lerTexto(caminho) {
251
+ return (await this.ler(caminho)).texto;
252
+ }
253
+ async digest(caminho) {
254
+ return (await this.ler(caminho)).digest;
255
+ }
256
+ async fingerprint(caminhos) {
257
+ const selecionados = caminhos
258
+ ? [...new Map(caminhos.map((caminho) => {
259
+ const absoluto = path.resolve(this.baseDiretorio, caminho);
260
+ return [chaveCaminho(absoluto), absoluto];
261
+ })).values()]
262
+ : this.arquivosCatalogados();
263
+ selecionados.sort((a, b) => chaveCaminho(a).localeCompare(chaveCaminho(b), "en"));
264
+ const itens = await mapearComLimite(selecionados, this.concorrencia, async (arquivo) => ({
265
+ caminho: chaveCaminho(arquivo).replace(/\\/g, "/"),
266
+ digest: await this.digest(arquivo),
267
+ }));
268
+ return `sha256:${createHash("sha256").update(JSON.stringify(itens)).digest("hex")}`;
269
+ }
270
+ obterFingerprint(caminhos) {
271
+ return this.fingerprint(caminhos);
272
+ }
273
+ emitir(tipo, arquivo) {
274
+ emitirEvento(this.observador, {
275
+ tipo,
276
+ caminho: arquivo ? path.resolve(this.baseDiretorio, arquivo) : undefined,
277
+ });
278
+ }
279
+ metricas() {
280
+ return { ...this.contadores };
281
+ }
282
+ snapshot() {
283
+ return {
284
+ schema: SCHEMA_SNAPSHOT_CATALOGO_DRIFT,
285
+ raizes: this.raizes.map((raiz) => this.relativoSeguro(raiz, true)),
286
+ extensoes: this.extensoesSnapshot ? [...this.extensoesSnapshot] : null,
287
+ diretoriosIgnorados: [...this.diretoriosIgnoradosSnapshot],
288
+ diretorios: [...this.diretoriosVisitados.entries()]
289
+ .map(([caminho, assinatura]) => ({
290
+ caminho: this.relativoSeguro(caminho, true),
291
+ assinatura: { ...assinatura },
292
+ }))
293
+ .sort((a, b) => a.caminho.localeCompare(b.caminho, "en")),
294
+ arquivos: [...this.registros.values()]
295
+ .map((registro) => ({
296
+ caminho: this.relativoSeguro(registro.caminho),
297
+ explicito: registro.explicito,
298
+ }))
299
+ .sort((a, b) => a.caminho.localeCompare(b.caminho, "en")),
300
+ };
301
+ }
302
+ async caminharRaizes(diretoriosIgnorados) {
303
+ let nivel = [...this.raizes];
304
+ const visitados = new Set();
305
+ while (nivel.length > 0) {
306
+ const nivelUnico = [...new Map(nivel.map((diretorio) => [chaveCaminho(diretorio), diretorio])).values()]
307
+ .filter((diretorio) => {
308
+ const chave = chaveCaminho(diretorio);
309
+ if (visitados.has(chave)) {
310
+ return false;
311
+ }
312
+ visitados.add(chave);
313
+ return true;
314
+ })
315
+ .sort((a, b) => a.localeCompare(b, "pt-BR"));
316
+ const proximos = await mapearComLimite(nivelUnico, this.concorrencia, async (diretorio) => this.visitarDiretorio(diretorio, diretoriosIgnorados));
317
+ nivel = proximos.flat();
318
+ }
319
+ }
320
+ async visitarDiretorio(diretorio, diretoriosIgnorados) {
321
+ if (!await this.caminhoPermaneceNoWorkspace(diretorio)) {
322
+ return [];
323
+ }
324
+ let informacao;
325
+ try {
326
+ informacao = await lstat(diretorio, { bigint: true });
327
+ }
328
+ catch (erro) {
329
+ if (erroEhAusencia(erro)) {
330
+ return [];
331
+ }
332
+ throw erro;
333
+ }
334
+ if (informacao.isSymbolicLink()) {
335
+ return [];
336
+ }
337
+ if (informacao.isFile()) {
338
+ this.adicionarArquivo(diretorio, false);
339
+ return [];
340
+ }
341
+ if (!informacao.isDirectory()) {
342
+ return [];
343
+ }
344
+ let entradas;
345
+ try {
346
+ entradas = await readdir(diretorio, { withFileTypes: true });
347
+ }
348
+ catch (erro) {
349
+ if (erroEhAusencia(erro)) {
350
+ return [];
351
+ }
352
+ throw erro;
353
+ }
354
+ const informacaoDepois = await lstat(diretorio, { bigint: true });
355
+ if (!informacaoDepois.isDirectory()
356
+ || informacaoDepois.isSymbolicLink()
357
+ || !assinaturasDiretorioIguais(assinaturaDiretorio(informacao), assinaturaDiretorio(informacaoDepois))) {
358
+ throw new Error(`catalogo_diretorio_instavel:${diretorio}`);
359
+ }
360
+ this.contadores.diretoriosVisitados += 1;
361
+ this.diretoriosVisitados.set(path.resolve(diretorio), assinaturaDiretorio(informacaoDepois));
362
+ emitirEvento(this.observador, {
363
+ tipo: "catalog.visit",
364
+ caminho: diretorio,
365
+ categoria: "diretorio",
366
+ });
367
+ const proximos = [];
368
+ for (const entrada of entradas.sort((a, b) => a.name.localeCompare(b.name, "pt-BR"))) {
369
+ if (entrada.isSymbolicLink()) {
370
+ continue;
371
+ }
372
+ const caminhoAtual = path.join(diretorio, entrada.name);
373
+ if (entrada.isDirectory()) {
374
+ if (!diretoriosIgnorados.has(entrada.name.toLowerCase())) {
375
+ proximos.push(caminhoAtual);
376
+ }
377
+ continue;
378
+ }
379
+ if (entrada.isFile()) {
380
+ this.adicionarArquivo(caminhoAtual, false);
381
+ }
382
+ }
383
+ return proximos;
384
+ }
385
+ adicionarArquivo(caminho, explicito, emitirVisita = true) {
386
+ const absoluto = path.resolve(caminho);
387
+ const extensao = path.extname(absoluto).toLowerCase();
388
+ if (this.extensoesPermitidas && !this.extensoesPermitidas.has(extensao)) {
389
+ return;
390
+ }
391
+ const chave = chaveCaminho(absoluto);
392
+ const existente = this.registros.get(chave);
393
+ const raizes = this.raizes.filter((raiz) => caminhoEstaDentro(raiz, absoluto));
394
+ if (existente) {
395
+ existente.explicito = existente.explicito || explicito;
396
+ for (const raiz of raizes) {
397
+ existente.raizes.add(raiz);
398
+ }
399
+ return;
400
+ }
401
+ this.registros.set(chave, {
402
+ caminho: absoluto,
403
+ extensao,
404
+ explicito,
405
+ raizes: new Set(raizes),
406
+ });
407
+ this.contadores.arquivosCatalogados += 1;
408
+ if (emitirVisita) {
409
+ emitirEvento(this.observador, {
410
+ tipo: "catalog.visit",
411
+ caminho: absoluto,
412
+ categoria: "arquivo",
413
+ });
414
+ }
415
+ }
416
+ async restaurarSnapshot(snapshot, diretoriosIgnorados, arquivosExplicitos) {
417
+ if (!registroObjeto(snapshot)
418
+ || snapshot.schema !== SCHEMA_SNAPSHOT_CATALOGO_DRIFT
419
+ || !Array.isArray(snapshot.raizes)
420
+ || !snapshot.raizes.every(diretorioRelativoSeguro)
421
+ || !Array.isArray(snapshot.diretoriosIgnorados)
422
+ || !snapshot.diretoriosIgnorados.every((item) => typeof item === "string")
423
+ || !(snapshot.extensoes === null
424
+ || (Array.isArray(snapshot.extensoes) && snapshot.extensoes.every((item) => typeof item === "string")))
425
+ || !Array.isArray(snapshot.diretorios)
426
+ || !Array.isArray(snapshot.arquivos)) {
427
+ return false;
428
+ }
429
+ const raizesAtuais = this.raizes.map((raiz) => this.relativoSeguro(raiz, true));
430
+ const ignoradosAtuais = [...diretoriosIgnorados].sort((a, b) => a.localeCompare(b, "en"));
431
+ if (JSON.stringify(snapshot.raizes) !== JSON.stringify(raizesAtuais)
432
+ || JSON.stringify(snapshot.extensoes) !== JSON.stringify(this.extensoesSnapshot)
433
+ || JSON.stringify(snapshot.diretoriosIgnorados) !== JSON.stringify(ignoradosAtuais)) {
434
+ return false;
435
+ }
436
+ const diretorios = snapshot.diretorios.map((entrada) => {
437
+ if (!registroObjeto(entrada)
438
+ || !diretorioRelativoSeguro(entrada.caminho)
439
+ || !assinaturaDiretorioValida(entrada.assinatura)) {
440
+ return undefined;
441
+ }
442
+ return {
443
+ caminho: path.resolve(this.baseDiretorio, entrada.caminho),
444
+ assinatura: entrada.assinatura,
445
+ };
446
+ });
447
+ if (diretorios.some((entrada) => !entrada)) {
448
+ return false;
449
+ }
450
+ if (diretorios.some((entrada) => entrada
451
+ && !this.caminhoPertenceRaizCatalogavel(entrada.caminho, diretoriosIgnorados, true))) {
452
+ return false;
453
+ }
454
+ const estadosDiretorios = await mapearComLimite(diretorios, this.concorrencia, async (entrada) => {
455
+ if (!await this.caminhoPermaneceNoWorkspace(entrada.caminho)) {
456
+ return false;
457
+ }
458
+ try {
459
+ const info = await lstat(entrada.caminho, { bigint: true });
460
+ this.contadores.diretoriosValidadosCache += 1;
461
+ return info.isDirectory()
462
+ && !info.isSymbolicLink()
463
+ && assinaturasDiretorioIguais(entrada.assinatura, assinaturaDiretorio(info));
464
+ }
465
+ catch {
466
+ return false;
467
+ }
468
+ });
469
+ if (estadosDiretorios.some((valido) => !valido)) {
470
+ this.contadores.diretoriosValidadosCache = 0;
471
+ return false;
472
+ }
473
+ const arquivos = snapshot.arquivos.map((entrada) => {
474
+ if (!registroObjeto(entrada)
475
+ || !caminhoRelativoSeguro(entrada.caminho)
476
+ || typeof entrada.explicito !== "boolean") {
477
+ return undefined;
478
+ }
479
+ return {
480
+ caminho: path.resolve(this.baseDiretorio, entrada.caminho),
481
+ explicito: entrada.explicito,
482
+ };
483
+ });
484
+ if (arquivos.some((entrada) => !entrada)) {
485
+ return false;
486
+ }
487
+ const arquivosTipados = arquivos;
488
+ const explicitosPermitidos = new Set(arquivosExplicitos.map((arquivo) => chaveCaminho(path.resolve(this.baseDiretorio, arquivo))));
489
+ if (arquivosTipados.some((entrada) => entrada.explicito
490
+ ? !explicitosPermitidos.has(chaveCaminho(entrada.caminho))
491
+ : !this.caminhoPertenceRaizCatalogavel(entrada.caminho, diretoriosIgnorados))) {
492
+ return false;
493
+ }
494
+ const caminhosRepresentados = new Set([
495
+ ...diretorios
496
+ .filter((entrada) => Boolean(entrada))
497
+ .map((entrada) => chaveCaminho(entrada.caminho)),
498
+ ...arquivosTipados.map((entrada) => chaveCaminho(entrada.caminho)),
499
+ ]);
500
+ if (this.raizes.some((raiz) => !caminhosRepresentados.has(chaveCaminho(raiz)))) {
501
+ // Uma raiz ausente nao pode produzir um snapshot eterno: a criacao
502
+ // posterior dessa raiz precisa forcar uma nova caminhada.
503
+ return false;
504
+ }
505
+ const estadosArquivos = await mapearComLimite(arquivosTipados, this.concorrencia, async (entrada) => {
506
+ if (!caminhoEstaDentro(this.baseDiretorio, entrada.caminho)
507
+ || !await this.caminhoPermaneceNoWorkspace(entrada.caminho)) {
508
+ return false;
509
+ }
510
+ try {
511
+ const info = await lstat(entrada.caminho);
512
+ return info.isFile()
513
+ && !info.isSymbolicLink()
514
+ && (!this.extensoesPermitidas
515
+ || this.extensoesPermitidas.has(path.extname(entrada.caminho).toLowerCase()));
516
+ }
517
+ catch {
518
+ return false;
519
+ }
520
+ });
521
+ if (estadosArquivos.some((valido) => !valido)) {
522
+ return false;
523
+ }
524
+ const diretoriosTipados = diretorios;
525
+ const diretoriosAindaEstaveis = await mapearComLimite(diretoriosTipados, this.concorrencia, async (entrada) => {
526
+ try {
527
+ const info = await lstat(entrada.caminho, { bigint: true });
528
+ return info.isDirectory()
529
+ && !info.isSymbolicLink()
530
+ && assinaturasDiretorioIguais(entrada.assinatura, assinaturaDiretorio(info));
531
+ }
532
+ catch {
533
+ return false;
534
+ }
535
+ });
536
+ if (diretoriosAindaEstaveis.some((valido) => !valido)) {
537
+ return false;
538
+ }
539
+ for (const entrada of arquivosTipados) {
540
+ this.adicionarArquivo(entrada.caminho, entrada.explicito, false);
541
+ }
542
+ for (const entrada of diretoriosTipados) {
543
+ this.diretoriosVisitados.set(entrada.caminho, { ...entrada.assinatura });
544
+ }
545
+ this.contadores.origem = "snapshot";
546
+ return true;
547
+ }
548
+ relativoSeguro(caminho, permitirBase = false) {
549
+ const relativo = path.relative(this.baseDiretorio, path.resolve(caminho)).replace(/\\/g, "/");
550
+ if (!(permitirBase ? diretorioRelativoSeguro(relativo || ".") : caminhoRelativoSeguro(relativo))) {
551
+ throw new Error(`caminho_snapshot_catalogo_invalido:${caminho}`);
552
+ }
553
+ return relativo || ".";
554
+ }
555
+ caminhoPertenceRaizCatalogavel(caminho, diretoriosIgnorados, incluirUltimoSegmento = false) {
556
+ return this.raizes.some((raiz) => {
557
+ if (!caminhoEstaDentro(raiz, caminho)) {
558
+ return false;
559
+ }
560
+ const segmentos = path.relative(raiz, caminho)
561
+ .split(path.sep)
562
+ .filter(Boolean);
563
+ if (!incluirUltimoSegmento) {
564
+ segmentos.pop();
565
+ }
566
+ return segmentos.every((segmento) => !diretoriosIgnorados.has(segmento.toLowerCase()));
567
+ });
568
+ }
569
+ async lerFisicamente(caminho) {
570
+ if (!await this.caminhoPermaneceNoWorkspace(caminho)) {
571
+ throw new Error(`Arquivo catalogado saiu do workspace real: ${caminho}`);
572
+ }
573
+ const informacaoAntes = await lstat(caminho, { bigint: true });
574
+ if (!informacaoAntes.isFile() || informacaoAntes.isSymbolicLink()) {
575
+ throw new Error(`Arquivo catalogado deixou de ser arquivo regular: ${caminho}`);
576
+ }
577
+ const handle = await open(caminho, "r");
578
+ let bytes;
579
+ try {
580
+ const informacaoHandleAntes = await handle.stat({ bigint: true });
581
+ emitirEvento(this.observador, {
582
+ tipo: "content.open",
583
+ caminho,
584
+ categoria: "arquivo",
585
+ });
586
+ const [informacaoCaminhoAberto, caminhoRealAberto] = await Promise.all([
587
+ lstat(caminho, { bigint: true }),
588
+ realpath(caminho),
589
+ ]);
590
+ const baseReal = await this.baseDiretorioReal;
591
+ if (!caminhoEstaDentro(baseReal, caminhoRealAberto)
592
+ || !identidadeArquivoEstavel(informacaoAntes, informacaoHandleAntes)
593
+ || !identidadeArquivoEstavel(informacaoHandleAntes, informacaoCaminhoAberto)) {
594
+ throw new Error(`Arquivo catalogado mudou durante abertura segura: ${caminho}`);
595
+ }
596
+ bytes = await handle.readFile();
597
+ const [informacaoHandleDepois, informacaoCaminhoDepois, caminhoRealDepois] = await Promise.all([
598
+ handle.stat({ bigint: true }),
599
+ lstat(caminho, { bigint: true }),
600
+ realpath(caminho),
601
+ ]);
602
+ if (!caminhoEstaDentro(baseReal, caminhoRealDepois)
603
+ || !identidadeArquivoEstavel(informacaoHandleAntes, informacaoHandleDepois)
604
+ || !identidadeArquivoEstavel(informacaoHandleDepois, informacaoCaminhoDepois)) {
605
+ throw new Error(`Arquivo catalogado mudou durante leitura segura: ${caminho}`);
606
+ }
607
+ }
608
+ finally {
609
+ await handle.close().catch(() => undefined);
610
+ }
611
+ const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
612
+ this.contadores.leiturasConteudo += 1;
613
+ this.contadores.bytesLidos += bytes.byteLength;
614
+ emitirEvento(this.observador, {
615
+ tipo: "content.read",
616
+ caminho,
617
+ categoria: "arquivo",
618
+ bytes: bytes.byteLength,
619
+ digest,
620
+ });
621
+ return {
622
+ caminho,
623
+ bytes,
624
+ texto: bytes.toString("utf8"),
625
+ digest,
626
+ };
627
+ }
628
+ async caminhoPermaneceNoWorkspace(caminho) {
629
+ const absoluto = path.resolve(caminho);
630
+ if (!caminhoEstaDentro(this.baseDiretorio, absoluto)) {
631
+ return false;
632
+ }
633
+ try {
634
+ const [baseReal, caminhoReal] = await Promise.all([
635
+ this.baseDiretorioReal,
636
+ realpath(absoluto),
637
+ ]);
638
+ return caminhoEstaDentro(baseReal, caminhoReal);
639
+ }
640
+ catch (erro) {
641
+ if (erroEhAusencia(erro)) {
642
+ return false;
643
+ }
644
+ throw erro;
645
+ }
646
+ }
647
+ }
648
+ export async function criarCatalogoDrift(opcoes) {
649
+ const ignoradosOrigem = opcoes.diretoriosIgnorados ?? [];
650
+ const ignorados = new Set([...ignoradosOrigem]
651
+ .map((diretorio) => diretorio.trim().toLowerCase())
652
+ .filter(Boolean));
653
+ const catalogo = new CatalogoDriftImplementacao(opcoes);
654
+ await catalogo.construir(opcoes.arquivos ?? [], ignorados, opcoes.snapshot);
655
+ return catalogo;
656
+ }
657
+ //# sourceMappingURL=driftCatalogo.js.map
@@ -0,0 +1,8 @@
1
+ import { type PoliticaCacheDrift, type ValorJson } from "./driftCache.js";
2
+ import { type CatalogoDrift, type OpcoesCatalogoDrift } from "./driftCatalogo.js";
3
+ export declare const SCHEMA_CACHE_CATALOGO_DRIFT: "sema.drift-catalog-cache/v1";
4
+ export interface OpcoesCatalogoPersistenteDrift {
5
+ politica: PoliticaCacheDrift;
6
+ identidade: ValorJson;
7
+ }
8
+ export declare function criarCatalogoPersistenteDrift(opcoes: OpcoesCatalogoDrift, opcoesCache: OpcoesCatalogoPersistenteDrift): Promise<CatalogoDrift>;