@semacode/cli 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/LICENSE +39 -39
  2. package/README.md +112 -41
  3. package/dist/agentContextPack.js +14 -10
  4. package/dist/agentEntryPoints.d.ts +5 -0
  5. package/dist/agentEntryPoints.js +102 -91
  6. package/dist/angular-consumer-standalone.d.ts +3 -1
  7. package/dist/angular-consumer-standalone.js +12 -8
  8. package/dist/bin.d.ts +6 -0
  9. package/dist/bin.js +130 -0
  10. package/dist/cliArgs.js +4 -4
  11. package/dist/cliControlError.d.ts +12 -0
  12. package/dist/cliControlError.js +47 -0
  13. package/dist/cliGrammar.d.ts +11 -0
  14. package/dist/cliGrammar.js +551 -0
  15. package/dist/cliHelp.d.ts +13 -0
  16. package/dist/cliHelp.js +19 -0
  17. package/dist/cliHelpTexto.d.ts +3 -0
  18. package/dist/cliHelpTexto.js +168 -0
  19. package/dist/cliRuntime.d.ts +2 -0
  20. package/dist/cliRuntime.js +31 -0
  21. package/dist/comandos.js +9 -22
  22. package/dist/contexto.js +5 -5
  23. package/dist/dev/index.d.ts +1 -0
  24. package/dist/dev/index.js +51 -9
  25. package/dist/discovery/catalog.js +3 -3
  26. package/dist/distribuicao/filesystemGlobal.d.ts +63 -0
  27. package/dist/distribuicao/filesystemGlobal.js +701 -0
  28. package/dist/distribuicao/index.d.ts +7 -0
  29. package/dist/distribuicao/index.js +227 -0
  30. package/dist/distribuicao/launcherGlobal.d.ts +53 -0
  31. package/dist/distribuicao/launcherGlobal.js +643 -0
  32. package/dist/distribuicao/launcherWindows.d.ts +28 -0
  33. package/dist/distribuicao/launcherWindows.js +326 -0
  34. package/dist/distribuicao/lockContextGlobal.d.ts +10 -0
  35. package/dist/distribuicao/lockContextGlobal.js +17 -0
  36. package/dist/distribuicao/lockGlobal.d.ts +6 -0
  37. package/dist/distribuicao/lockGlobal.js +520 -0
  38. package/dist/distribuicao/skillGlobal.d.ts +48 -0
  39. package/dist/distribuicao/skillGlobal.js +652 -0
  40. package/dist/distribuicao/skillIntentPolicy.d.ts +21 -0
  41. package/dist/distribuicao/skillIntentPolicy.js +115 -0
  42. package/dist/distribuicao/tipos.d.ts +50 -0
  43. package/dist/distribuicao/tipos.js +10 -0
  44. package/dist/distribuicao/versaoSemantica.d.ts +3 -0
  45. package/dist/distribuicao/versaoSemantica.js +62 -0
  46. package/dist/drift.part01.d.ts +34 -2
  47. package/dist/drift.part01.js +3 -0
  48. package/dist/drift.part04.d.ts +12 -2
  49. package/dist/drift.part04.js +89 -15
  50. package/dist/drift.part05.d.ts +2 -1
  51. package/dist/drift.part05.js +9 -6
  52. package/dist/drift.part06.d.ts +2 -1
  53. package/dist/drift.part06.js +115 -24
  54. package/dist/drift.part07.d.ts +11 -10
  55. package/dist/drift.part07.js +261 -255
  56. package/dist/drift.part08.d.ts +2 -1
  57. package/dist/drift.part08.js +35 -25
  58. package/dist/drift.part11.js +279 -32
  59. package/dist/drift.part12.d.ts +1 -1
  60. package/dist/drift.part12.js +196 -11
  61. package/dist/drift.part13.d.ts +18 -2
  62. package/dist/drift.part13.js +654 -76
  63. package/dist/drift.part14.js +21 -7
  64. package/dist/driftCacheCas.d.ts +43 -0
  65. package/dist/driftCacheCas.js +351 -0
  66. package/dist/driftCacheFilesystem.d.ts +64 -0
  67. package/dist/driftCacheFilesystem.js +313 -0
  68. package/dist/driftCacheModes.d.ts +38 -0
  69. package/dist/driftCacheModes.js +147 -0
  70. package/dist/driftCacheStore.d.ts +80 -0
  71. package/dist/driftCacheStore.js +603 -0
  72. package/dist/driftCatalogo.d.ts +65 -0
  73. package/dist/driftCatalogo.js +516 -0
  74. package/dist/driftEscopo.d.ts +21 -0
  75. package/dist/driftEscopo.js +647 -0
  76. package/dist/driftEscopoReferencias.d.ts +8 -0
  77. package/dist/driftEscopoReferencias.js +612 -0
  78. package/dist/driftOrcamento.d.ts +6 -0
  79. package/dist/driftOrcamento.js +20 -3
  80. package/dist/geracaoCore.js +4 -6
  81. package/dist/iaPrompts.d.ts +3 -3
  82. package/dist/iaPrompts.js +13 -14
  83. package/dist/index.d.ts +4 -1
  84. package/dist/index.js +6 -25
  85. package/dist/index.part01.d.ts +21 -14
  86. package/dist/index.part01.js +20 -165
  87. package/dist/index.part02.js +57 -43
  88. package/dist/index.part03.d.ts +23 -1
  89. package/dist/index.part03.js +83 -8
  90. package/dist/index.part04.d.ts +10 -2
  91. package/dist/index.part04.js +48 -14
  92. package/dist/index.part05.d.ts +1 -1
  93. package/dist/index.part05.js +109 -30
  94. package/dist/index.part06.js +34 -16
  95. package/dist/index.part07.js +31 -27
  96. package/dist/index.part08.d.ts +1 -1
  97. package/dist/index.part08.js +23 -28
  98. package/dist/init/templates.part01.js +393 -393
  99. package/dist/init/templates.part02.js +189 -189
  100. package/dist/initCommand.js +5 -1
  101. package/dist/initTemplatesBackend.js +73 -73
  102. package/dist/initTemplatesBase.js +7 -5
  103. package/dist/initTemplatesFlutter.js +137 -137
  104. package/dist/initTemplatesRuntime.js +350 -350
  105. package/dist/initTemplatesWeb.js +309 -309
  106. package/dist/pipelineConteudo/command.d.ts +1 -0
  107. package/dist/pipelineConteudo/command.js +4 -32
  108. package/dist/pipelineConteudo/help.d.ts +2 -0
  109. package/dist/pipelineConteudo/help.js +33 -0
  110. package/dist/profileAuthorCommand.js +3 -7
  111. package/dist/profileCommand.js +3 -7
  112. package/dist/projeto.d.ts +1 -1
  113. package/dist/projetoCarregar.d.ts +2 -2
  114. package/dist/projetoCarregar.js +69 -28
  115. package/dist/projetoCatalogo.d.ts +8 -0
  116. package/dist/projetoCatalogo.js +164 -0
  117. package/dist/projetoOrigens.d.ts +2 -0
  118. package/dist/projetoOrigens.js +65 -9
  119. package/dist/projetoTipos.d.ts +15 -5
  120. package/dist/resultadoCli.d.ts +20 -0
  121. package/dist/resultadoCli.js +57 -0
  122. package/dist/saidaCli.d.ts +35 -0
  123. package/dist/saidaCli.js +118 -0
  124. package/dist/sistemasInterativos/acceptanceEvidence.d.ts +22 -0
  125. package/dist/sistemasInterativos/acceptanceEvidence.js +345 -0
  126. package/dist/sistemasInterativos/engineObservation.d.ts +12 -0
  127. package/dist/sistemasInterativos/engineObservation.js +438 -0
  128. package/dist/sistemasInterativos/jobOrchestration.d.ts +2 -0
  129. package/dist/sistemasInterativos/jobOrchestration.js +204 -0
  130. package/dist/sistemasInterativos/operationPrimitives.d.ts +21 -0
  131. package/dist/sistemasInterativos/operationPrimitives.js +194 -0
  132. package/dist/sistemasInterativos/operations.d.ts +4 -39
  133. package/dist/sistemasInterativos/operations.js +5 -1164
  134. package/dist/skillCommand.d.ts +5 -0
  135. package/dist/skillCommand.js +88 -0
  136. package/dist/sync/index.js +35 -35
  137. package/docs/README.md +11 -7
  138. package/docs/ai-integration.md +18 -11
  139. package/docs/ai-onboarding.md +81 -62
  140. package/docs/ai-workflow.md +10 -4
  141. package/docs/auth.md +26 -26
  142. package/docs/cli.md +197 -79
  143. package/docs/commands.md +30 -6
  144. package/docs/database.md +15 -15
  145. package/docs/deploy.md +54 -41
  146. package/docs/documentation.md +11 -10
  147. package/docs/drift-cache.md +108 -0
  148. package/docs/env.md +16 -16
  149. package/docs/frontend.md +41 -41
  150. package/docs/getting-started.md +82 -57
  151. package/docs/i18n.md +15 -15
  152. package/docs/repositories.md +60 -43
  153. package/docs/rollback.md +38 -31
  154. package/docs/security.md +54 -32
  155. package/docs/sistemas-interativos.md +27 -27
  156. package/docs/support.md +8 -8
  157. package/docs/testing.md +123 -17
  158. package/docs/vocabulary.md +29 -29
  159. package/exemplos/agendamento.sema +1 -1
  160. package/exemplos/assinatura.sema +1 -1
  161. package/exemplos/auditoria.sema +1 -1
  162. package/exemplos/autenticacao.sema +1 -1
  163. package/exemplos/author_obra_comum.sema +1 -1
  164. package/exemplos/author_tema_sensivel.sema +1 -1
  165. package/exemplos/automacao.sema +1 -1
  166. package/exemplos/cadastro_usuario.sema +1 -1
  167. package/exemplos/calculadora.sema +1 -1
  168. package/exemplos/crud_simples.sema +1 -1
  169. package/exemplos/estoque.sema +1 -1
  170. package/exemplos/exportacao.sema +1 -1
  171. package/exemplos/fila.sema +1 -1
  172. package/exemplos/integracao_externa.sema +1 -1
  173. package/exemplos/multi_tenant.sema +1 -1
  174. package/exemplos/notificacao.sema +1 -1
  175. package/exemplos/operacao_estrategia.sema +132 -132
  176. package/exemplos/operacao_estrategia_discovery.sema +220 -220
  177. package/exemplos/operacao_estrategia_fluxo.sema +114 -114
  178. package/exemplos/operacao_estrategia_gates.sema +106 -106
  179. package/exemplos/operacao_estrategia_quarentena.sema +133 -133
  180. package/exemplos/operacao_estrategia_rollout.sema +124 -124
  181. package/exemplos/pagamento.sema +1 -1
  182. package/exemplos/pagamento_dominio.sema +1 -1
  183. package/exemplos/pedido.sema +1 -1
  184. package/exemplos/permissao.sema +1 -1
  185. package/exemplos/persistencia_vendor_first.sema +1 -1
  186. package/exemplos/profile_conversas.sema +165 -165
  187. package/exemplos/profile_game.sema +1 -1
  188. package/exemplos/profile_legal.sema +1 -1
  189. package/exemplos/profile_ops.sema +1 -1
  190. package/exemplos/profile_research.sema +1 -1
  191. package/exemplos/profile_software.sema +1 -1
  192. package/exemplos/profile_workflow_n8n.sema +1 -1
  193. package/exemplos/relatorio.sema +1 -1
  194. package/exemplos/replica_analitica_erp.sema +1 -1
  195. package/exemplos/testes_embutidos.sema +1 -1
  196. package/exemplos/tratamento_erro.sema +1 -1
  197. package/exemplos/upload_arquivo.sema +1 -1
  198. package/exemplos/webhook.sema +1 -1
  199. package/node_modules/@sema/gerador-cpp/dist/index.js +176 -176
  200. package/node_modules/@sema/gerador-css/dist/index.part01.js +10 -10
  201. package/node_modules/@sema/gerador-css/dist/index.part02.js +460 -460
  202. package/node_modules/@sema/gerador-dotnet/dist/index.js +169 -169
  203. package/node_modules/@sema/gerador-html/dist/index.js +17 -17
  204. package/node_modules/@sema/gerador-python/dist/index.part02.js +3 -3
  205. package/node_modules/@sema/gerador-typescript/dist/index.part02.js +11 -11
  206. package/node_modules/@sema/nucleo/dist/diagnosticos/melhorador.js +42 -42
  207. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +1 -0
  208. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +1 -0
  209. package/package.json +7 -2
  210. package/scripts/postinstall.mjs +148 -0
  211. package/skills/sema/SKILL.md +199 -0
  212. package/skills/sema/agents/openai.yaml +4 -0
  213. package/node_modules/@sema/gerador-cpp/dist/index.js.map +0 -1
  214. package/node_modules/@sema/gerador-css/dist/index.js.map +0 -1
  215. package/node_modules/@sema/gerador-css/dist/index.part01.js.map +0 -1
  216. package/node_modules/@sema/gerador-css/dist/index.part02.js.map +0 -1
  217. package/node_modules/@sema/gerador-dart/dist/index.js.map +0 -1
  218. package/node_modules/@sema/gerador-dotnet/dist/index.js.map +0 -1
  219. package/node_modules/@sema/gerador-html/dist/index.js.map +0 -1
  220. package/node_modules/@sema/gerador-javascript/dist/index.js.map +0 -1
  221. package/node_modules/@sema/gerador-lua/dist/index.js.map +0 -1
  222. package/node_modules/@sema/gerador-php/dist/index.js.map +0 -1
  223. package/node_modules/@sema/gerador-python/dist/index.js.map +0 -1
  224. package/node_modules/@sema/gerador-python/dist/index.part01.js.map +0 -1
  225. package/node_modules/@sema/gerador-python/dist/index.part02.js.map +0 -1
  226. package/node_modules/@sema/gerador-typescript/dist/index.js.map +0 -1
  227. package/node_modules/@sema/gerador-typescript/dist/index.part01.js.map +0 -1
  228. package/node_modules/@sema/gerador-typescript/dist/index.part02.js.map +0 -1
  229. package/node_modules/@sema/gerador-typescript/dist/index.part03.js.map +0 -1
  230. package/node_modules/@sema/nucleo/dist/ast/tipos.js.map +0 -1
  231. package/node_modules/@sema/nucleo/dist/diagnosticos/index.js.map +0 -1
  232. package/node_modules/@sema/nucleo/dist/diagnosticos/melhorador.js.map +0 -1
  233. package/node_modules/@sema/nucleo/dist/diagnosticos/snippets.js.map +0 -1
  234. package/node_modules/@sema/nucleo/dist/formatador/index.js.map +0 -1
  235. package/node_modules/@sema/nucleo/dist/formatador/index.part01.js.map +0 -1
  236. package/node_modules/@sema/nucleo/dist/formatador/index.part02.js.map +0 -1
  237. package/node_modules/@sema/nucleo/dist/index.js.map +0 -1
  238. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +0 -1
  239. package/node_modules/@sema/nucleo/dist/ir/conversor.part01.js.map +0 -1
  240. package/node_modules/@sema/nucleo/dist/ir/conversor.part02.js.map +0 -1
  241. package/node_modules/@sema/nucleo/dist/ir/conversor.part03.js.map +0 -1
  242. package/node_modules/@sema/nucleo/dist/ir/modelos.js.map +0 -1
  243. package/node_modules/@sema/nucleo/dist/lexer/lexer.js.map +0 -1
  244. package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +0 -1
  245. package/node_modules/@sema/nucleo/dist/parser/parser.declaracoes.js.map +0 -1
  246. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +0 -1
  247. package/node_modules/@sema/nucleo/dist/parser/parser.modulo.js.map +0 -1
  248. package/node_modules/@sema/nucleo/dist/parser/parser.part01.js.map +0 -1
  249. package/node_modules/@sema/nucleo/dist/parser/parser.part02.js.map +0 -1
  250. package/node_modules/@sema/nucleo/dist/parser/parser.part03.js.map +0 -1
  251. package/node_modules/@sema/nucleo/dist/persistencia/contratos.js.map +0 -1
  252. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +0 -1
  253. package/node_modules/@sema/nucleo/dist/semantico/analisador.part01.js.map +0 -1
  254. package/node_modules/@sema/nucleo/dist/semantico/analisador.part02.js.map +0 -1
  255. package/node_modules/@sema/nucleo/dist/semantico/analisador.part03.js.map +0 -1
  256. package/node_modules/@sema/nucleo/dist/semantico/analisador.part04.js.map +0 -1
  257. package/node_modules/@sema/nucleo/dist/semantico/analisador.part05.js.map +0 -1
  258. package/node_modules/@sema/nucleo/dist/semantico/analisador.part06.js.map +0 -1
  259. package/node_modules/@sema/nucleo/dist/semantico/analisador.part07.js.map +0 -1
  260. package/node_modules/@sema/nucleo/dist/semantico/analisador.part08.js.map +0 -1
  261. package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +0 -1
  262. package/node_modules/@sema/nucleo/dist/semantico/estruturas.part01.js.map +0 -1
  263. package/node_modules/@sema/nucleo/dist/semantico/estruturas.part02.js.map +0 -1
  264. package/node_modules/@sema/nucleo/dist/semantico/orcamentoSemantico.js.map +0 -1
  265. package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +0 -1
  266. package/node_modules/@sema/nucleo/dist/util/arquivos.js.map +0 -1
  267. package/node_modules/@sema/padroes/dist/index.js.map +0 -1
@@ -0,0 +1,701 @@
1
+ // SEMA-GOVERNED: sema.produto.distribuicao_global
2
+ // Descrição: fornece primitivas confinadas para escrita atômica fora do workspace, somente em destinos globais permitidos.
3
+ import { constants } from "node:fs";
4
+ import { chmod, lstat, mkdir, open, readdir, realpath, rename, rm, stat, unlink, } from "node:fs/promises";
5
+ import { createHash, randomUUID } from "node:crypto";
6
+ import path from "node:path";
7
+ import { confirmarFencingLockDistribuicaoGlobal } from "./lockContextGlobal.js";
8
+ export class FalhaDistribuicaoGlobal extends Error {
9
+ estado;
10
+ codigo;
11
+ constructor(estado, codigo) {
12
+ super(codigo);
13
+ this.estado = estado;
14
+ this.codigo = codigo;
15
+ this.name = "FalhaDistribuicaoGlobal";
16
+ }
17
+ }
18
+ function identidadeDe(info) {
19
+ return { dev: info.dev, ino: info.ino };
20
+ }
21
+ function mesmaIdentidade(a, b) {
22
+ return a.dev === b.dev && a.ino === b.ino;
23
+ }
24
+ export function mesmaIdentidadeDiretorio(a, b) {
25
+ return a.dev === b.dev && a.ino === b.ino;
26
+ }
27
+ export function erroEhPermissao(erro) {
28
+ return ["EACCES", "EPERM", "EROFS"].includes(erro.code ?? "");
29
+ }
30
+ export function falhaParaResultado(erro) {
31
+ if (erro instanceof FalhaDistribuicaoGlobal)
32
+ return erro;
33
+ if (erroEhPermissao(erro)) {
34
+ return new FalhaDistribuicaoGlobal("PERMISSION_DENIED", "ERRO_PERMISSAO");
35
+ }
36
+ return new FalhaDistribuicaoGlobal("BROKEN_TARGET", "AMBIENTE_INVALIDO");
37
+ }
38
+ export async function lstatOuNull(caminho) {
39
+ try {
40
+ return await lstat(caminho, { bigint: true });
41
+ }
42
+ catch (erro) {
43
+ if (["ENOENT", "ENOTDIR"].includes(erro.code ?? ""))
44
+ return null;
45
+ throw erro;
46
+ }
47
+ }
48
+ function caminhoComparavel(caminho) {
49
+ const normalizado = path.normalize(path.resolve(caminho));
50
+ return process.platform === "win32" ? normalizado.toLocaleLowerCase("en-US") : normalizado;
51
+ }
52
+ export function caminhosIguais(a, b) {
53
+ return caminhoComparavel(a) === caminhoComparavel(b);
54
+ }
55
+ export function caminhoContido(base, alvo) {
56
+ const relativo = path.relative(path.resolve(base), path.resolve(alvo));
57
+ return relativo === "" || (relativo !== ".."
58
+ && !relativo.startsWith(`..${path.sep}`)
59
+ && !path.isAbsolute(relativo));
60
+ }
61
+ async function validarDiretorioReal(caminho) {
62
+ const info = await lstat(caminho, { bigint: true });
63
+ if (info.isSymbolicLink()) {
64
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
65
+ }
66
+ if (!info.isDirectory()) {
67
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "COMPONENTE_NAO_DIRETORIO");
68
+ }
69
+ const real = await realpath(caminho);
70
+ if (!caminhosIguais(real, caminho)) {
71
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
72
+ }
73
+ return info;
74
+ }
75
+ export async function identidadeDiretorioSeguro(caminho) {
76
+ const info = await validarDiretorioReal(caminho);
77
+ return identidadeDe(info);
78
+ }
79
+ export async function resolverHomeReal(diretorioUsuario) {
80
+ if (!path.isAbsolute(diretorioUsuario) || diretorioUsuario.includes("\0")) {
81
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "AMBIENTE_INVALIDO");
82
+ }
83
+ try {
84
+ const absoluto = path.resolve(diretorioUsuario);
85
+ await validarDiretorioReal(absoluto);
86
+ return await realpath(absoluto);
87
+ }
88
+ catch (erro) {
89
+ throw falhaParaResultado(erro);
90
+ }
91
+ }
92
+ export function resolverDentroDaHome(homeReal, ...segmentos) {
93
+ if (segmentos.some((segmento) => (!segmento
94
+ || segmento.includes("\0")
95
+ || path.isAbsolute(segmento)
96
+ || segmento === ".."
97
+ || segmento.includes("/")
98
+ || segmento.includes("\\")))) {
99
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
100
+ }
101
+ const destino = path.resolve(homeReal, ...segmentos);
102
+ if (!caminhoContido(homeReal, destino) || caminhosIguais(homeReal, destino)) {
103
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
104
+ }
105
+ return destino;
106
+ }
107
+ export async function validarCadeiaExistente(homeReal, destino) {
108
+ if (!caminhoContido(homeReal, destino)) {
109
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
110
+ }
111
+ let atual = homeReal;
112
+ for (const segmento of path.relative(homeReal, destino).split(path.sep).filter(Boolean)) {
113
+ atual = path.join(atual, segmento);
114
+ const info = await lstatOuNull(atual);
115
+ if (!info)
116
+ return "ausente";
117
+ await validarDiretorioReal(atual);
118
+ }
119
+ return "completa";
120
+ }
121
+ export async function garantirDiretoriosSeguros(homeReal, destino) {
122
+ if (!caminhoContido(homeReal, destino)) {
123
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
124
+ }
125
+ let atual = homeReal;
126
+ for (const segmento of path.relative(homeReal, destino).split(path.sep).filter(Boolean)) {
127
+ atual = path.join(atual, segmento);
128
+ let info = await lstatOuNull(atual);
129
+ if (!info) {
130
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
131
+ try {
132
+ await mkdir(atual, { mode: 0o700 });
133
+ }
134
+ catch (erro) {
135
+ if (erro.code !== "EEXIST")
136
+ throw erro;
137
+ }
138
+ info = await lstatOuNull(atual);
139
+ }
140
+ if (!info)
141
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
142
+ await validarDiretorioReal(atual);
143
+ }
144
+ }
145
+ export async function validarArquivoRegular(caminho, basePermitida) {
146
+ if (basePermitida && !caminhoContido(basePermitida, caminho)) {
147
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
148
+ }
149
+ const info = await lstat(caminho, { bigint: true });
150
+ if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1n) {
151
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
152
+ }
153
+ const real = await realpath(caminho);
154
+ if (!caminhosIguais(real, caminho)) {
155
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
156
+ }
157
+ return info;
158
+ }
159
+ export async function identidadeArquivoSeguro(caminho, basePermitida) {
160
+ return identidadeDe(await validarArquivoRegular(caminho, basePermitida));
161
+ }
162
+ export async function removerArquivoSeguro(homeReal, caminho, identidadeEsperada) {
163
+ if (!caminhoContido(homeReal, caminho)) {
164
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
165
+ }
166
+ const pai = path.dirname(caminho);
167
+ const identidadePai = await identidadeDiretorioSeguro(pai);
168
+ const identidadeInicial = await identidadeArquivoSeguro(caminho, homeReal);
169
+ if (identidadeEsperada && !mesmaIdentidade(identidadeEsperada, identidadeInicial)) {
170
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
171
+ }
172
+ const [arquivoAntes, paiAntes] = await Promise.all([
173
+ identidadeArquivoSeguro(caminho, homeReal),
174
+ identidadeDiretorioSeguro(pai),
175
+ ]);
176
+ if (!mesmaIdentidade(identidadeInicial, arquivoAntes)
177
+ || !mesmaIdentidade(identidadePai, paiAntes)) {
178
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
179
+ }
180
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
181
+ await unlink(caminho);
182
+ const paiDepois = await identidadeDiretorioSeguro(pai);
183
+ if (!mesmaIdentidade(identidadePai, paiDepois)) {
184
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
185
+ }
186
+ }
187
+ export async function lerArquivoSeguro(caminho, limiteBytes = 2 * 1024 * 1024, basePermitida) {
188
+ const infoAntes = await validarArquivoRegular(caminho, basePermitida);
189
+ if (infoAntes.size > BigInt(limiteBytes)) {
190
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "PACOTE_INVALIDO");
191
+ }
192
+ const handle = await open(caminho, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
193
+ try {
194
+ const infoHandle = await handle.stat({ bigint: true });
195
+ if (!mesmaIdentidade(identidadeDe(infoAntes), identidadeDe(infoHandle))) {
196
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
197
+ }
198
+ const bytes = await handle.readFile();
199
+ const infoDepois = await validarArquivoRegular(caminho, basePermitida);
200
+ if (bytes.byteLength > limiteBytes
201
+ || !mesmaIdentidade(identidadeDe(infoAntes), identidadeDe(infoDepois))) {
202
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
203
+ }
204
+ return bytes;
205
+ }
206
+ finally {
207
+ await handle.close().catch(() => undefined);
208
+ }
209
+ }
210
+ async function confirmarTemporario(caminho, handle, identidade) {
211
+ const [porCaminho, porHandle] = await Promise.all([
212
+ lstat(caminho, { bigint: true }),
213
+ handle.stat({ bigint: true }),
214
+ ]);
215
+ if (!porCaminho.isFile()
216
+ || porCaminho.isSymbolicLink()
217
+ || porCaminho.nlink !== 1n
218
+ || porHandle.nlink !== 1n
219
+ || !mesmaIdentidade(identidade, identidadeDe(porCaminho))
220
+ || !mesmaIdentidade(identidade, identidadeDe(porHandle))) {
221
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
222
+ }
223
+ }
224
+ async function removerTemporarioSeMesmo(caminho, identidade) {
225
+ if (!identidade)
226
+ return;
227
+ try {
228
+ const info = await lstat(caminho, { bigint: true });
229
+ if (info.isFile()
230
+ && !info.isSymbolicLink()
231
+ && info.nlink === 1n
232
+ && mesmaIdentidade(identidade, identidadeDe(info))) {
233
+ await unlink(caminho);
234
+ }
235
+ }
236
+ catch {
237
+ // Limpeza best effort nunca remove um caminho cuja identidade mudou.
238
+ }
239
+ }
240
+ export async function escreverArquivoAtomico(homeReal, destino, conteudo, modo) {
241
+ if (!caminhoContido(homeReal, destino)) {
242
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
243
+ }
244
+ const diretorio = path.dirname(destino);
245
+ await garantirDiretoriosSeguros(homeReal, diretorio);
246
+ const identidadePai = await identidadeDiretorioSeguro(diretorio);
247
+ const existenteAntes = await lstatOuNull(destino);
248
+ if (existenteAntes)
249
+ await validarArquivoRegular(destino, homeReal);
250
+ const temporario = path.join(diretorio, `.${path.basename(destino)}.sema-${randomUUID()}.tmp`);
251
+ let handle;
252
+ let identidade;
253
+ let renomeado = false;
254
+ try {
255
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
256
+ handle = await open(temporario, "wx", 0o600);
257
+ const info = await handle.stat({ bigint: true });
258
+ identidade = identidadeDe(info);
259
+ await confirmarTemporario(temporario, handle, identidade);
260
+ await handle.writeFile(conteudo);
261
+ await handle.sync();
262
+ await confirmarTemporario(temporario, handle, identidade);
263
+ const existenteAgora = await lstatOuNull(destino);
264
+ if ((existenteAntes === null) !== (existenteAgora === null)
265
+ || (existenteAntes && existenteAgora
266
+ && !mesmaIdentidade(identidadeDe(existenteAntes), identidadeDe(existenteAgora)))) {
267
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
268
+ }
269
+ const paiAntesRename = await identidadeDiretorioSeguro(diretorio);
270
+ if (!mesmaIdentidadeDiretorio(identidadePai, paiAntesRename)) {
271
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
272
+ }
273
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
274
+ await rename(temporario, destino);
275
+ renomeado = true;
276
+ const paiDepoisRename = await identidadeDiretorioSeguro(diretorio);
277
+ if (!mesmaIdentidadeDiretorio(identidadePai, paiDepoisRename)) {
278
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
279
+ }
280
+ if (process.platform !== "win32") {
281
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
282
+ await chmod(destino, modo);
283
+ }
284
+ await validarArquivoRegular(destino, homeReal);
285
+ }
286
+ finally {
287
+ await handle?.close().catch(() => undefined);
288
+ if (!renomeado)
289
+ await removerTemporarioSeMesmo(temporario, identidade);
290
+ }
291
+ }
292
+ export function nomeTemporarioDiretorio(prefixo) {
293
+ return `.sema-${prefixo}-${randomUUID()}`;
294
+ }
295
+ export async function removerDiretorioTemporarioGerenciado(homeReal, caminho, identidadeEsperada) {
296
+ const nome = path.basename(caminho);
297
+ if (!caminhoContido(homeReal, caminho) || !/^\.sema-(?:stage|backup)-[a-f0-9-]{36}$/u.test(nome)) {
298
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
299
+ }
300
+ const pai = path.dirname(caminho);
301
+ const identidadePai = await identidadeDiretorioSeguro(pai);
302
+ const identidadeInicial = await identidadeDiretorioSeguro(caminho);
303
+ if (identidadeEsperada
304
+ && !mesmaIdentidadeDiretorio(identidadeEsperada, identidadeInicial)) {
305
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
306
+ }
307
+ await validarArvoreSemLinks(caminho);
308
+ const [identidadeAntesRm, identidadePaiAntesRm] = await Promise.all([
309
+ identidadeDiretorioSeguro(caminho),
310
+ identidadeDiretorioSeguro(pai),
311
+ ]);
312
+ if (!mesmaIdentidadeDiretorio(identidadeInicial, identidadeAntesRm)
313
+ || !mesmaIdentidadeDiretorio(identidadePai, identidadePaiAntesRm)) {
314
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
315
+ }
316
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
317
+ await rm(caminho, { recursive: true, force: true });
318
+ const identidadePaiDepoisRm = await identidadeDiretorioSeguro(pai);
319
+ if (!mesmaIdentidadeDiretorio(identidadePai, identidadePaiDepoisRm)) {
320
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
321
+ }
322
+ }
323
+ async function validarArvoreSemLinks(caminho) {
324
+ const info = await lstat(caminho, { bigint: true });
325
+ if (info.isSymbolicLink()) {
326
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
327
+ }
328
+ if (info.isDirectory()) {
329
+ await validarDiretorioReal(caminho);
330
+ const entradas = await readdir(caminho);
331
+ for (const entrada of entradas) {
332
+ await validarArvoreSemLinks(path.join(caminho, entrada));
333
+ }
334
+ return;
335
+ }
336
+ if (!info.isFile() || info.nlink !== 1n) {
337
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
338
+ }
339
+ const real = await realpath(caminho);
340
+ if (!caminhosIguais(real, caminho)) {
341
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "SYMLINK_OU_JUNCTION");
342
+ }
343
+ }
344
+ export async function moverDiretorioSeguro(homeReal, origem, destino, identidadeEsperada) {
345
+ if (!caminhoContido(homeReal, origem)
346
+ || !caminhoContido(homeReal, destino)
347
+ || !caminhosIguais(path.dirname(origem), path.dirname(destino))) {
348
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "CAMINHO_FORA_DA_HOME");
349
+ }
350
+ const pai = path.dirname(origem);
351
+ const identidadePai = await identidadeDiretorioSeguro(pai);
352
+ const identidadeOrigem = await identidadeDiretorioSeguro(origem);
353
+ if (identidadeEsperada
354
+ && !mesmaIdentidadeDiretorio(identidadeEsperada, identidadeOrigem)) {
355
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
356
+ }
357
+ if (await lstatOuNull(destino)) {
358
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
359
+ }
360
+ const [origemAntesRename, paiAntesRename] = await Promise.all([
361
+ identidadeDiretorioSeguro(origem),
362
+ identidadeDiretorioSeguro(pai),
363
+ ]);
364
+ if (!mesmaIdentidadeDiretorio(identidadeOrigem, origemAntesRename)
365
+ || !mesmaIdentidadeDiretorio(identidadePai, paiAntesRename)) {
366
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
367
+ }
368
+ await confirmarFencingLockDistribuicaoGlobal(homeReal);
369
+ await rename(origem, destino);
370
+ const [destinoDepoisRename, paiDepoisRename] = await Promise.all([
371
+ identidadeDiretorioSeguro(destino),
372
+ identidadeDiretorioSeguro(pai),
373
+ ]);
374
+ if (!mesmaIdentidadeDiretorio(identidadeOrigem, destinoDepoisRename)
375
+ || !mesmaIdentidadeDiretorio(identidadePai, paiDepoisRename)) {
376
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
377
+ }
378
+ return destinoDepoisRename;
379
+ }
380
+ export async function diretorioExisteSeguro(caminho) {
381
+ const info = await lstatOuNull(caminho);
382
+ if (!info)
383
+ return false;
384
+ await validarDiretorioReal(caminho);
385
+ return true;
386
+ }
387
+ export async function statDiretorioSeguro(caminho) {
388
+ const info = await stat(caminho, { bigint: true });
389
+ if (!info.isDirectory()) {
390
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "COMPONENTE_NAO_DIRETORIO");
391
+ }
392
+ return validarDiretorioReal(caminho);
393
+ }
394
+ const JOURNAL_LAUNCHER = ".sema-launcher-journal";
395
+ const PADRAO_TEMP_TRANSACAO = /^\.sema-(stage|backup)-([a-f0-9-]{36})$/u;
396
+ const PADRAO_ARTEFATO_LAUNCHER = /^(?:sema|sema\.cmd|sema\.ps1|sema-managed\.ps1|sema-launcher\.receipt|\.sema-launcher-[a-f0-9]{64}\.ps1)$/u;
397
+ const LIMITE_TRANSACAO_BYTES = 512 * 1024;
398
+ function sha256Bytes(valor) {
399
+ return createHash("sha256").update(valor).digest("hex");
400
+ }
401
+ function serializarReceitado(base) {
402
+ const canonico = JSON.stringify(base);
403
+ return Buffer.from(JSON.stringify({ ...base, receipt: sha256Bytes(canonico) }), "utf8");
404
+ }
405
+ function parseReceitado(bytes) {
406
+ if (bytes.length > LIMITE_TRANSACAO_BYTES) {
407
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
408
+ }
409
+ let valor;
410
+ try {
411
+ valor = JSON.parse(bytes.toString("utf8"));
412
+ }
413
+ catch {
414
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
415
+ }
416
+ if (!valor || Array.isArray(valor) || typeof valor !== "object") {
417
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
418
+ }
419
+ const { receipt, ...base } = valor;
420
+ if (typeof receipt !== "string" || !/^[a-f0-9]{64}$/u.test(receipt)
421
+ || sha256Bytes(JSON.stringify(base)) !== receipt) {
422
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
423
+ }
424
+ return valor;
425
+ }
426
+ function validarNomeArtefatoLauncher(nome) {
427
+ if (!PADRAO_ARTEFATO_LAUNCHER.test(nome)) {
428
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
429
+ }
430
+ }
431
+ function validarPayloadTransacao(bytes, tipo, nonce) {
432
+ const payload = parseReceitado(bytes);
433
+ if (payload.schema !== "sema.launcher.files/v1" || payload.tipo !== tipo
434
+ || payload.nonce !== nonce || !Array.isArray(payload.itens)) {
435
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
436
+ }
437
+ const nomes = new Set();
438
+ for (const item of payload.itens) {
439
+ if (!item || typeof item.nome !== "string"
440
+ || (item.conteudo !== null && typeof item.conteudo !== "string")) {
441
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
442
+ }
443
+ validarNomeArtefatoLauncher(item.nome);
444
+ if (nomes.has(item.nome)) {
445
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
446
+ }
447
+ nomes.add(item.nome);
448
+ if (tipo === "stage" && item.conteudo === null) {
449
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
450
+ }
451
+ if (item.conteudo !== null) {
452
+ const decodificado = Buffer.from(item.conteudo, "base64");
453
+ if (decodificado.toString("base64") !== item.conteudo) {
454
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
455
+ }
456
+ }
457
+ }
458
+ return payload;
459
+ }
460
+ function validarJournalTransacao(bytes) {
461
+ const journal = parseReceitado(bytes);
462
+ const stage = PADRAO_TEMP_TRANSACAO.exec(journal.stage ?? "");
463
+ const backup = PADRAO_TEMP_TRANSACAO.exec(journal.backup ?? "");
464
+ if (journal.schema !== "sema.launcher.journal/v1"
465
+ || !["prepared", "committed"].includes(journal.fase)
466
+ || !/^[a-f0-9-]{36}$/u.test(journal.nonce)
467
+ || stage?.[1] !== "stage" || backup?.[1] !== "backup"
468
+ || stage[2] !== journal.nonce || backup[2] !== journal.nonce
469
+ || !/^[a-f0-9]{64}$/u.test(journal.stageSha256)
470
+ || !/^[a-f0-9]{64}$/u.test(journal.backupSha256)) {
471
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
472
+ }
473
+ return journal;
474
+ }
475
+ async function removerArquivoGerenciadoExato(homeReal, caminho, esperado) {
476
+ if (!await lstatOuNull(caminho))
477
+ return false;
478
+ const atual = await lerArquivoSeguro(caminho, LIMITE_TRANSACAO_BYTES, homeReal);
479
+ if (!atual.equals(esperado)) {
480
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
481
+ }
482
+ await removerArquivoSeguro(homeReal, caminho, await identidadeArquivoSeguro(caminho, homeReal));
483
+ return true;
484
+ }
485
+ async function limparTemporariosTransacaoOrfaos(homeReal, diretorio) {
486
+ if (!await lstatOuNull(diretorio))
487
+ return false;
488
+ let alterado = false;
489
+ for (const entrada of await readdir(diretorio, { withFileTypes: true })) {
490
+ const match = PADRAO_TEMP_TRANSACAO.exec(entrada.name);
491
+ if (!match || !entrada.isFile())
492
+ continue;
493
+ const caminho = path.join(diretorio, entrada.name);
494
+ try {
495
+ const bytes = await lerArquivoSeguro(caminho, LIMITE_TRANSACAO_BYTES, homeReal);
496
+ validarPayloadTransacao(bytes, match[1], match[2] ?? "");
497
+ await removerArquivoGerenciadoExato(homeReal, caminho, bytes);
498
+ alterado = true;
499
+ }
500
+ catch {
501
+ // Nome parecido sem recibo e identidade válidos pertence ao usuário e é preservado.
502
+ }
503
+ }
504
+ return alterado;
505
+ }
506
+ export async function prepararTransacaoArquivosLauncher(homeReal, diretorio, artefatos) {
507
+ await garantirDiretoriosSeguros(homeReal, diretorio);
508
+ const journal = path.join(diretorio, JOURNAL_LAUNCHER);
509
+ if (await lstatOuNull(journal)) {
510
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
511
+ }
512
+ const nonce = randomUUID();
513
+ const stage = path.join(diretorio, `.sema-stage-${nonce}`);
514
+ const backup = path.join(diretorio, `.sema-backup-${nonce}`);
515
+ if (await lstatOuNull(stage) || await lstatOuNull(backup)) {
516
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
517
+ }
518
+ const nomes = new Set();
519
+ for (const artefato of artefatos) {
520
+ validarNomeArtefatoLauncher(artefato.nome);
521
+ if (nomes.has(artefato.nome)) {
522
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "AMBIENTE_INVALIDO");
523
+ }
524
+ nomes.add(artefato.nome);
525
+ }
526
+ const itensBackup = [];
527
+ for (const artefato of artefatos) {
528
+ const destino = path.join(diretorio, artefato.nome);
529
+ const antes = await lstatOuNull(destino)
530
+ ? await lerArquivoSeguro(destino, LIMITE_TRANSACAO_BYTES, homeReal)
531
+ : undefined;
532
+ itensBackup.push({ nome: artefato.nome, conteudo: antes?.toString("base64") ?? null });
533
+ }
534
+ const stageBytes = serializarReceitado({
535
+ schema: "sema.launcher.files/v1",
536
+ tipo: "stage",
537
+ nonce,
538
+ itens: artefatos.map((item) => ({ nome: item.nome, conteudo: item.conteudoDepois.toString("base64") })),
539
+ });
540
+ const backupBytes = serializarReceitado({
541
+ schema: "sema.launcher.files/v1",
542
+ tipo: "backup",
543
+ nonce,
544
+ itens: itensBackup,
545
+ });
546
+ const baseJournal = {
547
+ schema: "sema.launcher.journal/v1",
548
+ nonce,
549
+ stage: path.basename(stage),
550
+ stageSha256: sha256Bytes(stageBytes),
551
+ backup: path.basename(backup),
552
+ backupSha256: sha256Bytes(backupBytes),
553
+ };
554
+ const journalPrepared = serializarReceitado({ ...baseJournal, fase: "prepared" });
555
+ const journalCommitted = serializarReceitado({ ...baseJournal, fase: "committed" });
556
+ try {
557
+ await escreverArquivoAtomico(homeReal, stage, stageBytes, 0o600);
558
+ await escreverArquivoAtomico(homeReal, backup, backupBytes, 0o600);
559
+ await escreverArquivoAtomico(homeReal, journal, journalPrepared, 0o600);
560
+ }
561
+ catch (erro) {
562
+ await Promise.allSettled([
563
+ removerArquivoGerenciadoExato(homeReal, stage, stageBytes),
564
+ removerArquivoGerenciadoExato(homeReal, backup, backupBytes),
565
+ ]);
566
+ throw erro;
567
+ }
568
+ return { homeReal, diretorio, journal, stage, backup, journalPrepared, journalCommitted, stageBytes, backupBytes };
569
+ }
570
+ async function carregarTransacaoArquivosLauncher(homeReal, diretorio) {
571
+ const caminhoJournal = path.join(diretorio, JOURNAL_LAUNCHER);
572
+ if (!await lstatOuNull(caminhoJournal))
573
+ return null;
574
+ const journalBytes = await lerArquivoSeguro(caminhoJournal, LIMITE_TRANSACAO_BYTES, homeReal);
575
+ const journal = validarJournalTransacao(journalBytes);
576
+ const caminhoStage = path.join(diretorio, journal.stage);
577
+ const caminhoBackup = path.join(diretorio, journal.backup);
578
+ const [stageBytes, backupBytes] = await Promise.all([
579
+ lerArquivoSeguro(caminhoStage, LIMITE_TRANSACAO_BYTES, homeReal),
580
+ lerArquivoSeguro(caminhoBackup, LIMITE_TRANSACAO_BYTES, homeReal),
581
+ ]);
582
+ if (sha256Bytes(stageBytes) !== journal.stageSha256
583
+ || sha256Bytes(backupBytes) !== journal.backupSha256) {
584
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
585
+ }
586
+ const stage = validarPayloadTransacao(stageBytes, "stage", journal.nonce);
587
+ const backup = validarPayloadTransacao(backupBytes, "backup", journal.nonce);
588
+ if (stage.itens.length !== backup.itens.length
589
+ || stage.itens.some((item, indice) => item.nome !== backup.itens[indice]?.nome)) {
590
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "RECIBO_INVALIDO");
591
+ }
592
+ return { journal, journalBytes, stage, stageBytes, backup, backupBytes };
593
+ }
594
+ async function restaurarItemTransacao(homeReal, diretorio, depois, antes) {
595
+ const destino = path.join(diretorio, depois.nome);
596
+ const esperadoDepois = Buffer.from(depois.conteudo ?? "", "base64");
597
+ const esperadoAntes = antes.conteudo === null ? null : Buffer.from(antes.conteudo, "base64");
598
+ const existe = await lstatOuNull(destino) !== null;
599
+ if (!existe && esperadoAntes) {
600
+ await escreverArquivoAtomico(homeReal, destino, esperadoAntes, depois.nome === "sema" ? 0o755 : 0o600);
601
+ return;
602
+ }
603
+ if (!existe)
604
+ return;
605
+ const atual = await lerArquivoSeguro(destino, LIMITE_TRANSACAO_BYTES, homeReal);
606
+ if (esperadoAntes?.equals(atual))
607
+ return;
608
+ if (!atual.equals(esperadoDepois)) {
609
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
610
+ }
611
+ if (esperadoAntes) {
612
+ await escreverArquivoAtomico(homeReal, destino, esperadoAntes, depois.nome === "sema" ? 0o755 : 0o600);
613
+ }
614
+ else {
615
+ await removerArquivoSeguro(homeReal, destino, await identidadeArquivoSeguro(destino, homeReal));
616
+ }
617
+ }
618
+ async function confirmarItemRestaurado(homeReal, diretorio, antes) {
619
+ const destino = path.join(diretorio, antes.nome);
620
+ const existe = await lstatOuNull(destino) !== null;
621
+ if (antes.conteudo === null) {
622
+ if (existe)
623
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "ROLLBACK_FALHOU");
624
+ return;
625
+ }
626
+ if (!existe || !(await lerArquivoSeguro(destino, LIMITE_TRANSACAO_BYTES, homeReal))
627
+ .equals(Buffer.from(antes.conteudo, "base64"))) {
628
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "ROLLBACK_FALHOU");
629
+ }
630
+ }
631
+ async function limparArquivosDaTransacao(homeReal, diretorio, carregada) {
632
+ const journal = path.join(diretorio, JOURNAL_LAUNCHER);
633
+ const stage = path.join(diretorio, carregada.journal.stage);
634
+ const backup = path.join(diretorio, carregada.journal.backup);
635
+ try {
636
+ await removerArquivoGerenciadoExato(homeReal, journal, carregada.journalBytes);
637
+ }
638
+ catch {
639
+ return true;
640
+ }
641
+ const limpezas = await Promise.allSettled([
642
+ removerArquivoGerenciadoExato(homeReal, stage, carregada.stageBytes),
643
+ removerArquivoGerenciadoExato(homeReal, backup, carregada.backupBytes),
644
+ ]);
645
+ return limpezas.some((item) => item.status === "rejected");
646
+ }
647
+ export async function recuperarTransacaoArquivosLauncher(homeReal, diretorio) {
648
+ if (!await lstatOuNull(diretorio))
649
+ return { alterado: false, limpezaPendente: false };
650
+ const carregada = await carregarTransacaoArquivosLauncher(homeReal, diretorio);
651
+ if (!carregada) {
652
+ const alterado = await limparTemporariosTransacaoOrfaos(homeReal, diretorio);
653
+ return { alterado, limpezaPendente: false };
654
+ }
655
+ if (carregada.journal.fase === "prepared") {
656
+ const restauracoes = await Promise.allSettled(carregada.stage.itens.map((item, indice) => (restaurarItemTransacao(homeReal, diretorio, item, carregada.backup.itens[indice]))));
657
+ const verificacoes = await Promise.allSettled(carregada.backup.itens.map((item) => (confirmarItemRestaurado(homeReal, diretorio, item))));
658
+ if ([...restauracoes, ...verificacoes].some((item) => item.status === "rejected")) {
659
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "ROLLBACK_FALHOU");
660
+ }
661
+ }
662
+ const limpezaPendente = await limparArquivosDaTransacao(homeReal, diretorio, carregada);
663
+ return { alterado: carregada.journal.fase === "prepared", limpezaPendente };
664
+ }
665
+ export async function concluirTransacaoArquivosLauncher(transacao) {
666
+ const atual = await lerArquivoSeguro(transacao.journal, LIMITE_TRANSACAO_BYTES, transacao.homeReal);
667
+ if (!atual.equals(transacao.journalPrepared)) {
668
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "DESTINO_ALTERADO");
669
+ }
670
+ await escreverArquivoAtomico(transacao.homeReal, transacao.journal, transacao.journalCommitted, 0o600);
671
+ const carregada = await carregarTransacaoArquivosLauncher(transacao.homeReal, transacao.diretorio);
672
+ if (!carregada || carregada.journal.fase !== "committed") {
673
+ throw new FalhaDistribuicaoGlobal("BROKEN_TARGET", "ROLLBACK_FALHOU");
674
+ }
675
+ return {
676
+ limpezaPendente: await limparArquivosDaTransacao(transacao.homeReal, transacao.diretorio, carregada),
677
+ };
678
+ }
679
+ export async function transacaoArquivosLauncherPendente(homeReal, diretorio) {
680
+ if (!await lstatOuNull(diretorio))
681
+ return false;
682
+ if (await lstatOuNull(path.join(diretorio, JOURNAL_LAUNCHER))) {
683
+ await carregarTransacaoArquivosLauncher(homeReal, diretorio);
684
+ return true;
685
+ }
686
+ for (const entrada of await readdir(diretorio, { withFileTypes: true })) {
687
+ const match = PADRAO_TEMP_TRANSACAO.exec(entrada.name);
688
+ if (!entrada.isFile() || !match)
689
+ continue;
690
+ try {
691
+ const bytes = await lerArquivoSeguro(path.join(diretorio, entrada.name), LIMITE_TRANSACAO_BYTES, homeReal);
692
+ validarPayloadTransacao(bytes, match[1], match[2] ?? "");
693
+ return true;
694
+ }
695
+ catch {
696
+ // Falso padrão não gerenciado não influencia o estado público.
697
+ }
698
+ }
699
+ return false;
700
+ }
701
+ //# sourceMappingURL=filesystemGlobal.js.map