kaven-cli 0.3.0 → 0.4.0-alpha.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.
@@ -170,6 +170,21 @@ async function initProject(projectName, options) {
170
170
  console.error(chalk_1.default.gray(error instanceof Error ? error.message : String(error)));
171
171
  }
172
172
  }
173
+ // Install kaven-squad (optional, non-fatal)
174
+ if (options.withSquad) {
175
+ const squadSpinner = (0, ora_1.default)("Installing kaven-squad...").start();
176
+ const squadResult = await initializer.installSquad(targetDir);
177
+ if (squadResult.installed) {
178
+ squadSpinner.succeed("kaven-squad installed in squads/kaven-squad/");
179
+ }
180
+ else if (squadResult.reason === "already-exists") {
181
+ squadSpinner.info("kaven-squad already installed — skipping");
182
+ }
183
+ else {
184
+ squadSpinner.warn(`Could not install kaven-squad automatically (${squadResult.reason})`);
185
+ console.log(chalk_1.default.yellow(" ⚠ Install manually inside the project: *download-squad kaven-squad"));
186
+ }
187
+ }
173
188
  // Health check
174
189
  const healthCheckSpinner = (0, ora_1.default)("Running health check...").start();
175
190
  const health = await initializer.healthCheck(targetDir);
@@ -193,6 +208,12 @@ async function initProject(projectName, options) {
193
208
  console.log(chalk_1.default.cyan(" pnpm dev"));
194
209
  console.log();
195
210
  console.log(chalk_1.default.gray("For more help, visit: https://docs.kaven.sh/getting-started"));
211
+ if (options.withSquad) {
212
+ console.log();
213
+ console.log(chalk_1.default.bold("AIOX Squad next step:"));
214
+ console.log(chalk_1.default.cyan(" node /path/to/aiox-core/bin/aiox.js install --quiet --merge"));
215
+ console.log(chalk_1.default.gray(" (replace /path/to/aiox-core with your AIOX installation path)"));
216
+ }
196
217
  // Save project defaults to config for future use
197
218
  await ConfigManager_1.configManager.initialize();
198
219
  try {
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.moduleActivate = moduleActivate;
7
+ exports.moduleDeactivate = moduleDeactivate;
8
+ exports.moduleListActivation = moduleListActivation;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const SchemaActivator_1 = require("../../core/SchemaActivator");
12
+ const TelemetryBuffer_1 = require("../../infrastructure/TelemetryBuffer");
13
+ // ============================================================
14
+ // Helpers
15
+ // ============================================================
16
+ function findModuleDef(moduleId) {
17
+ return SchemaActivator_1.KAVEN_MODULES.find((m) => m.id === moduleId.toLowerCase());
18
+ }
19
+ function assertSchemaExists(exists) {
20
+ if (!exists) {
21
+ console.error(chalk_1.default.red("\nNão encontrei packages/database/prisma/schema.extended.prisma."));
22
+ console.error(chalk_1.default.gray("Execute este comando na raiz de um projeto Kaven."));
23
+ process.exit(1);
24
+ }
25
+ }
26
+ // ============================================================
27
+ // kaven module activate <name>
28
+ // ============================================================
29
+ async function moduleActivate(moduleName, projectRoot) {
30
+ const telemetry = TelemetryBuffer_1.TelemetryBuffer.getInstance();
31
+ const startTime = Date.now();
32
+ telemetry.capture("cli.module.activate.start", { moduleName });
33
+ const root = projectRoot ?? process.cwd();
34
+ const activator = new SchemaActivator_1.SchemaActivator(root);
35
+ const spinner = (0, ora_1.default)(`Ativando módulo ${moduleName}...`).start();
36
+ try {
37
+ // 1. Validar que o schema existe
38
+ const exists = await activator.exists();
39
+ spinner.stop();
40
+ assertSchemaExists(exists);
41
+ // 2. Validar que o módulo é conhecido
42
+ const def = findModuleDef(moduleName);
43
+ if (!def) {
44
+ console.error(chalk_1.default.red(`\nMódulo desconhecido: "${moduleName}".`));
45
+ console.error(chalk_1.default.gray(`Módulos disponíveis: ${SchemaActivator_1.KAVEN_MODULES.map((m) => m.id).join(", ")}`));
46
+ process.exit(1);
47
+ }
48
+ // 3. Verificar dependências
49
+ if (def.dependsOn.length > 0) {
50
+ spinner.start("Verificando dependências...");
51
+ const depStatuses = [];
52
+ for (const depId of def.dependsOn) {
53
+ const depDef = findModuleDef(depId);
54
+ if (!depDef)
55
+ continue;
56
+ const status = await activator.getModuleStatus(depDef);
57
+ depStatuses.push(status);
58
+ }
59
+ const missing = depStatuses.filter((s) => !s.active);
60
+ if (missing.length > 0) {
61
+ spinner.stop();
62
+ console.error(chalk_1.default.red(`\nDependências inativas para "${moduleName}": ${missing.map((m) => m.id).join(", ")}`));
63
+ console.error(chalk_1.default.gray(`Ative as dependências primeiro:\n${missing.map((m) => ` kaven module activate ${m.id}`).join("\n")}`));
64
+ process.exit(1);
65
+ }
66
+ spinner.stop();
67
+ }
68
+ // 4. Verificar se já está ativo
69
+ const current = await activator.getModuleStatus(def);
70
+ if (current.active) {
71
+ console.log(chalk_1.default.yellow(`\nMódulo "${def.label}" já está ativo.`));
72
+ return;
73
+ }
74
+ // 5. Ativar
75
+ spinner.start(`Descomentando modelos de ${def.label} no schema...`);
76
+ await activator.activateModule(def);
77
+ spinner.succeed(chalk_1.default.green(`\nMódulo ${def.label} ativado. ${def.models.length} models adicionados: ${def.models.join(", ")}`));
78
+ console.log(chalk_1.default.cyan("\nSugestão: Execute `pnpm db:generate && pnpm db:migrate` para aplicar as mudanças."));
79
+ telemetry.capture("cli.module.activate.success", { moduleName: def.id, models: def.models.length }, Date.now() - startTime);
80
+ await telemetry.flush();
81
+ }
82
+ catch (error) {
83
+ spinner.fail(chalk_1.default.red(`Falha ao ativar módulo: ${error instanceof Error ? error.message : String(error)}`));
84
+ telemetry.capture("cli.module.activate.error", { moduleName, error: error.message }, Date.now() - startTime);
85
+ await telemetry.flush();
86
+ process.exit(1);
87
+ }
88
+ }
89
+ // ============================================================
90
+ // kaven module deactivate <name>
91
+ // ============================================================
92
+ async function moduleDeactivate(moduleName, projectRoot) {
93
+ const telemetry = TelemetryBuffer_1.TelemetryBuffer.getInstance();
94
+ const startTime = Date.now();
95
+ telemetry.capture("cli.module.deactivate.start", { moduleName });
96
+ const root = projectRoot ?? process.cwd();
97
+ const activator = new SchemaActivator_1.SchemaActivator(root);
98
+ const spinner = (0, ora_1.default)(`Desativando módulo ${moduleName}...`).start();
99
+ try {
100
+ // 1. Validar que o schema existe
101
+ const exists = await activator.exists();
102
+ spinner.stop();
103
+ assertSchemaExists(exists);
104
+ // 2. Validar módulo
105
+ const def = findModuleDef(moduleName);
106
+ if (!def) {
107
+ console.error(chalk_1.default.red(`\nMódulo desconhecido: "${moduleName}".`));
108
+ console.error(chalk_1.default.gray(`Módulos disponíveis: ${SchemaActivator_1.KAVEN_MODULES.map((m) => m.id).join(", ")}`));
109
+ process.exit(1);
110
+ }
111
+ // 3. Verificar se outros módulos ativos dependem deste
112
+ spinner.start("Verificando dependências reversas...");
113
+ const dependents = [];
114
+ for (const candidate of SchemaActivator_1.KAVEN_MODULES) {
115
+ if (candidate.id === def.id)
116
+ continue;
117
+ if (!candidate.dependsOn.includes(def.id))
118
+ continue;
119
+ const status = await activator.getModuleStatus(candidate);
120
+ if (status.active) {
121
+ dependents.push(candidate.id);
122
+ }
123
+ }
124
+ if (dependents.length > 0) {
125
+ spinner.stop();
126
+ console.error(chalk_1.default.red(`\nNão é possível desativar "${moduleName}": os seguintes módulos dependem dele e estão ativos:`));
127
+ console.error(chalk_1.default.gray(` ${dependents.join(", ")}`));
128
+ console.error(chalk_1.default.gray(`Desative-os primeiro:\n${dependents.map((d) => ` kaven module deactivate ${d}`).join("\n")}`));
129
+ process.exit(1);
130
+ }
131
+ // 4. Verificar se já está inativo
132
+ const current = await activator.getModuleStatus(def);
133
+ if (!current.active) {
134
+ spinner.stop();
135
+ console.log(chalk_1.default.yellow(`\nMódulo "${def.label}" já está inativo.`));
136
+ return;
137
+ }
138
+ // 5. Desativar
139
+ spinner.start(`Comentando modelos de ${def.label} no schema...`);
140
+ await activator.deactivateModule(def);
141
+ spinner.succeed(chalk_1.default.green(`\nMódulo ${def.label} desativado com sucesso.`));
142
+ console.log(chalk_1.default.cyan("\nSugestão: Execute `pnpm db:generate && pnpm db:migrate` para aplicar as mudanças."));
143
+ telemetry.capture("cli.module.deactivate.success", { moduleName: def.id }, Date.now() - startTime);
144
+ await telemetry.flush();
145
+ }
146
+ catch (error) {
147
+ spinner.fail(chalk_1.default.red(`Falha ao desativar módulo: ${error instanceof Error ? error.message : String(error)}`));
148
+ telemetry.capture("cli.module.deactivate.error", { moduleName, error: error.message }, Date.now() - startTime);
149
+ await telemetry.flush();
150
+ process.exit(1);
151
+ }
152
+ }
153
+ // ============================================================
154
+ // kaven module list
155
+ // ============================================================
156
+ async function moduleListActivation(projectRoot) {
157
+ const root = projectRoot ?? process.cwd();
158
+ const activator = new SchemaActivator_1.SchemaActivator(root);
159
+ const schemaExists = await activator.exists();
160
+ // Cabeçalho da tabela
161
+ const COL = {
162
+ module: 14,
163
+ status: 10,
164
+ models: 44,
165
+ deps: 20,
166
+ };
167
+ const header = chalk_1.default.bold("Module".padEnd(COL.module)) +
168
+ chalk_1.default.bold("Status".padEnd(COL.status)) +
169
+ chalk_1.default.bold("Models".padEnd(COL.models)) +
170
+ chalk_1.default.bold("Depends on");
171
+ const divider = "─".repeat(COL.module + COL.status + COL.models + COL.deps);
172
+ console.log();
173
+ console.log(chalk_1.default.blue("Módulos Kaven disponíveis\n"));
174
+ console.log(header);
175
+ console.log(chalk_1.default.gray(divider));
176
+ if (!schemaExists) {
177
+ for (const def of SchemaActivator_1.KAVEN_MODULES) {
178
+ const modCol = def.id.padEnd(COL.module);
179
+ const statusCol = chalk_1.default.gray("unknown".padEnd(COL.status));
180
+ const modelsCol = def.models.join(", ").padEnd(COL.models);
181
+ const depsCol = def.dependsOn.length > 0 ? def.dependsOn.join(", ") : "—";
182
+ console.log(`${modCol}${statusCol}${modelsCol}${depsCol}`);
183
+ }
184
+ console.log();
185
+ console.log(chalk_1.default.yellow("Atenção: schema.extended.prisma não encontrado. Status não pode ser determinado."));
186
+ console.log(chalk_1.default.gray("Execute este comando na raiz de um projeto Kaven para ver o status real."));
187
+ return;
188
+ }
189
+ for (const def of SchemaActivator_1.KAVEN_MODULES) {
190
+ const status = await activator.getModuleStatus(def);
191
+ const modCol = def.id.padEnd(COL.module);
192
+ const statusText = status.active ? "active" : "inactive";
193
+ const statusColored = status.active
194
+ ? chalk_1.default.green(statusText.padEnd(COL.status))
195
+ : chalk_1.default.gray(statusText.padEnd(COL.status));
196
+ const modelsCol = def.models.join(", ").padEnd(COL.models);
197
+ const depsCol = def.dependsOn.length > 0 ? def.dependsOn.join(", ") : "—";
198
+ console.log(`${modCol}${statusColored}${modelsCol}${depsCol}`);
199
+ }
200
+ console.log();
201
+ console.log(chalk_1.default.gray("Core (sempre ativo): Tenant, User, Role, Capability, AuthSession, AuditLog"));
202
+ console.log();
203
+ console.log(chalk_1.default.gray("Para ativar: kaven module activate <name> | Para desativar: kaven module deactivate <name>"));
204
+ }
@@ -28,9 +28,9 @@ class ModuleDoctor {
28
28
  async checkAnchors() {
29
29
  const results = [];
30
30
  const expectedAnchors = [
31
- { file: "apps/api/src/index.ts", anchor: "// [ANCHOR:ROUTES]" },
32
- { file: "apps/api/src/index.ts", anchor: "// [ANCHOR:MIDDLEWARE]" },
33
- { file: "apps/admin/app/layout.tsx", anchor: "// [ANCHOR:NAV_ITEMS]" },
31
+ { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_IMPORTS]" },
32
+ { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_HOOKS]" },
33
+ { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_REGISTRATION]" },
34
34
  ];
35
35
  for (const { file, anchor } of expectedAnchors) {
36
36
  const filePath = path_1.default.join(this.projectRoot, file);
@@ -433,7 +433,7 @@ class ModuleDoctor {
433
433
  return results;
434
434
  }
435
435
  async readKavenConfig() {
436
- const configPath = path_1.default.join(this.projectRoot, "kaven.config.json");
436
+ const configPath = path_1.default.join(this.projectRoot, "kaven.json");
437
437
  if (!(await fs_extra_1.default.pathExists(configPath))) {
438
438
  return { modules: [] };
439
439
  }
@@ -8,6 +8,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const child_process_1 = require("child_process");
10
10
  const TEMPLATE_REPO = "https://github.com/kaven-co/kaven-template.git";
11
+ const KAVEN_SQUAD_REPO = "https://github.com/bychrisr/kaven-squad";
11
12
  /** Run a shell command via spawn, returning exit code. */
12
13
  function runCommand(cmd, args, cwd, onData) {
13
14
  return new Promise((resolve, reject) => {
@@ -93,6 +94,33 @@ class ProjectInitializer {
93
94
  await runCommand("git", ["add", "."], targetDir);
94
95
  await runCommand("git", ["commit", "-m", "chore: initial kaven setup"], targetDir);
95
96
  }
97
+ /**
98
+ * Clone kaven-squad into squads/kaven-squad/ inside the project.
99
+ * Returns { installed: true } on success, { installed: false, reason } on failure.
100
+ * Never throws — squad installation is non-fatal.
101
+ */
102
+ async installSquad(targetDir) {
103
+ const squadsDir = path_1.default.join(targetDir, "squads");
104
+ const squadDir = path_1.default.join(squadsDir, "kaven-squad");
105
+ // Squad already present — skip
106
+ if (await fs_extra_1.default.pathExists(squadDir)) {
107
+ return { installed: false, reason: "already-exists" };
108
+ }
109
+ await fs_extra_1.default.ensureDir(squadsDir);
110
+ const exitCode = await runCommand("git", ["clone", "--depth", "1", KAVEN_SQUAD_REPO, squadDir], process.cwd());
111
+ if (exitCode !== 0) {
112
+ return {
113
+ installed: false,
114
+ reason: `git clone exited with code ${exitCode}`,
115
+ };
116
+ }
117
+ // Remove .git — squad history not needed in user project
118
+ const squadGitDir = path_1.default.join(squadDir, ".git");
119
+ if (await fs_extra_1.default.pathExists(squadGitDir)) {
120
+ await fs_extra_1.default.remove(squadGitDir);
121
+ }
122
+ return { installed: true };
123
+ }
96
124
  /** Health check after project initialization. */
97
125
  async healthCheck(targetDir) {
98
126
  const issues = [];
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SchemaActivator = exports.KAVEN_MODULES = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ exports.KAVEN_MODULES = [
10
+ {
11
+ id: "billing",
12
+ label: "Billing",
13
+ description: "Faturamento, assinaturas e pagamentos",
14
+ models: ["Invoice", "Order", "Subscription", "Plan", "Payment", "Product"],
15
+ enums: [],
16
+ dependsOn: [],
17
+ },
18
+ {
19
+ id: "projects",
20
+ label: "Projects",
21
+ description: "Gestão de projetos e tasks",
22
+ models: ["Project", "Task"],
23
+ enums: ["ProjectStatus", "TaskStatus", "TaskPriority"],
24
+ dependsOn: [],
25
+ },
26
+ {
27
+ id: "notifications",
28
+ label: "Notifications",
29
+ description: "Notificações e preferências de usuário",
30
+ models: ["Notification", "UserPreference"],
31
+ enums: [],
32
+ dependsOn: [],
33
+ },
34
+ ];
35
+ // ============================================================
36
+ // Marcadores de seção no schema
37
+ // ============================================================
38
+ const BEGIN_MARKER = (moduleId) => `// [KAVEN_MODULE:${moduleId.toUpperCase()} BEGIN]`;
39
+ const END_MARKER = (moduleId) => `// [KAVEN_MODULE:${moduleId.toUpperCase()} END]`;
40
+ // ============================================================
41
+ // SchemaActivator — lê/escreve schema.extended.prisma
42
+ // ============================================================
43
+ class SchemaActivator {
44
+ constructor(projectRoot) {
45
+ this.schemaPath = path_1.default.join(projectRoot, "packages", "database", "prisma", "schema.extended.prisma");
46
+ }
47
+ /** Verifica se o schema existe no projeto */
48
+ async exists() {
49
+ return fs_extra_1.default.pathExists(this.schemaPath);
50
+ }
51
+ /** Caminho absoluto do schema */
52
+ get path() {
53
+ return this.schemaPath;
54
+ }
55
+ /** Lê o conteúdo atual do schema */
56
+ async readSchema() {
57
+ return fs_extra_1.default.readFile(this.schemaPath, "utf-8");
58
+ }
59
+ /** Persiste o conteúdo no schema */
60
+ async writeSchema(content) {
61
+ await fs_extra_1.default.writeFile(this.schemaPath, content, "utf-8");
62
+ }
63
+ /**
64
+ * Detecta se um módulo está ativo no schema.
65
+ *
66
+ * Estratégia:
67
+ * 1. Se existirem marcadores BEGIN/END: verifica se o conteúdo dentro
68
+ * dos marcadores NÃO está completamente comentado.
69
+ * 2. Se não houver marcadores: verifica se algum dos models principais
70
+ * do módulo está presente e não comentado no arquivo.
71
+ */
72
+ async getModuleStatus(def) {
73
+ const content = await this.readSchema();
74
+ const begin = BEGIN_MARKER(def.id);
75
+ const end = END_MARKER(def.id);
76
+ const hasMarkers = content.includes(begin) && content.includes(end);
77
+ let active = false;
78
+ if (hasMarkers) {
79
+ const block = this.extractBlock(content, def.id);
80
+ active = block !== null && this.isBlockActive(block);
81
+ }
82
+ else {
83
+ // Sem marcadores: verifica presença de pelo menos um model descomentado
84
+ active = def.models.some((modelName) => this.isModelActive(content, modelName));
85
+ }
86
+ return {
87
+ id: def.id,
88
+ label: def.label,
89
+ description: def.description,
90
+ models: def.models,
91
+ dependsOn: def.dependsOn,
92
+ active,
93
+ hasMarkers,
94
+ };
95
+ }
96
+ /** Ativa um módulo: se tem marcadores, descomenta o bloco; senão, injeta o bloco */
97
+ async activateModule(def) {
98
+ const content = await this.readSchema();
99
+ const begin = BEGIN_MARKER(def.id);
100
+ const end = END_MARKER(def.id);
101
+ if (content.includes(begin) && content.includes(end)) {
102
+ const updated = this.uncommentBlock(content, def.id);
103
+ await this.writeSchema(updated);
104
+ return;
105
+ }
106
+ // Módulo não tem seção marcada — sem template para injetar
107
+ throw new Error(`O módulo "${def.id}" não possui uma seção marcada (BEGIN/END) no schema.\n` +
108
+ `Adicione o bloco do módulo manualmente com os marcadores:\n` +
109
+ ` ${begin}\n` +
110
+ ` ... models do módulo ...\n` +
111
+ ` ${end}`);
112
+ }
113
+ /** Desativa um módulo: comenta todos os models do bloco */
114
+ async deactivateModule(def) {
115
+ const content = await this.readSchema();
116
+ const begin = BEGIN_MARKER(def.id);
117
+ const end = END_MARKER(def.id);
118
+ if (!content.includes(begin) || !content.includes(end)) {
119
+ throw new Error(`O módulo "${def.id}" não possui marcadores BEGIN/END no schema. ` +
120
+ `Não é possível desativar automaticamente.`);
121
+ }
122
+ const updated = this.commentBlock(content, def.id);
123
+ await this.writeSchema(updated);
124
+ }
125
+ // ──────────────────────────────────────────────
126
+ // Helpers de manipulação de blocos
127
+ // ──────────────────────────────────────────────
128
+ /**
129
+ * Extrai o conteúdo entre os marcadores BEGIN e END (exclusive).
130
+ * Retorna null se os marcadores não forem encontrados.
131
+ */
132
+ extractBlock(content, moduleId) {
133
+ const begin = BEGIN_MARKER(moduleId);
134
+ const end = END_MARKER(moduleId);
135
+ const lines = content.split("\n");
136
+ let beginIdx = -1;
137
+ let endIdx = -1;
138
+ for (let i = 0; i < lines.length; i++) {
139
+ if (lines[i].includes(begin))
140
+ beginIdx = i;
141
+ if (lines[i].includes(end) && beginIdx !== -1) {
142
+ endIdx = i;
143
+ break;
144
+ }
145
+ }
146
+ if (beginIdx === -1 || endIdx === -1)
147
+ return null;
148
+ return lines.slice(beginIdx + 1, endIdx).join("\n");
149
+ }
150
+ /**
151
+ * Verifica se um bloco tem ao menos uma linha não-comentada relevante
152
+ * (ignora linhas vazias e comentários simples).
153
+ */
154
+ isBlockActive(block) {
155
+ return block.split("\n").some((line) => {
156
+ const trimmed = line.trim();
157
+ return (trimmed.length > 0 &&
158
+ !trimmed.startsWith("//") &&
159
+ !trimmed.startsWith("/*") &&
160
+ !trimmed.startsWith("*"));
161
+ });
162
+ }
163
+ /**
164
+ * Verifica se um model específico está ativo (não comentado) no schema.
165
+ * Procura pela linha `model ModelName {` sem `//` antes.
166
+ */
167
+ isModelActive(content, modelName) {
168
+ const lines = content.split("\n");
169
+ for (const line of lines) {
170
+ const trimmed = line.trim();
171
+ if (trimmed.startsWith(`model ${modelName}`) &&
172
+ !line.trimStart().startsWith("//")) {
173
+ return true;
174
+ }
175
+ }
176
+ return false;
177
+ }
178
+ /**
179
+ * Comenta todas as linhas não-comentadas dentro do bloco BEGIN/END.
180
+ */
181
+ commentBlock(content, moduleId) {
182
+ const begin = BEGIN_MARKER(moduleId);
183
+ const end = END_MARKER(moduleId);
184
+ const lines = content.split("\n");
185
+ let inBlock = false;
186
+ const result = [];
187
+ for (const line of lines) {
188
+ if (line.includes(begin)) {
189
+ inBlock = true;
190
+ result.push(line);
191
+ continue;
192
+ }
193
+ if (line.includes(end)) {
194
+ inBlock = false;
195
+ result.push(line);
196
+ continue;
197
+ }
198
+ if (inBlock) {
199
+ const trimmed = line.trim();
200
+ if (trimmed.length === 0) {
201
+ result.push(line);
202
+ }
203
+ else if (trimmed.startsWith("//")) {
204
+ result.push(line); // já comentado
205
+ }
206
+ else {
207
+ result.push(`// ${line}`);
208
+ }
209
+ }
210
+ else {
211
+ result.push(line);
212
+ }
213
+ }
214
+ return result.join("\n");
215
+ }
216
+ /**
217
+ * Remove `// ` do início das linhas dentro do bloco BEGIN/END.
218
+ */
219
+ uncommentBlock(content, moduleId) {
220
+ const begin = BEGIN_MARKER(moduleId);
221
+ const end = END_MARKER(moduleId);
222
+ const lines = content.split("\n");
223
+ let inBlock = false;
224
+ const result = [];
225
+ for (const line of lines) {
226
+ if (line.includes(begin)) {
227
+ inBlock = true;
228
+ result.push(line);
229
+ continue;
230
+ }
231
+ if (line.includes(end)) {
232
+ inBlock = false;
233
+ result.push(line);
234
+ continue;
235
+ }
236
+ if (inBlock) {
237
+ // Remove exatamente um nível de comentário `// ` ou `//`
238
+ if (line.trimStart().startsWith("// ")) {
239
+ const indent = line.length - line.trimStart().length;
240
+ result.push(line.slice(0, indent) + line.trimStart().slice(3));
241
+ }
242
+ else if (line.trimStart().startsWith("//")) {
243
+ const indent = line.length - line.trimStart().length;
244
+ result.push(line.slice(0, indent) + line.trimStart().slice(2));
245
+ }
246
+ else {
247
+ result.push(line);
248
+ }
249
+ }
250
+ else {
251
+ result.push(line);
252
+ }
253
+ }
254
+ return result.join("\n");
255
+ }
256
+ }
257
+ exports.SchemaActivator = SchemaActivator;
@@ -33,9 +33,8 @@ function decodeSignature(encoded) {
33
33
  * Verify Ed25519 signature against a SHA-256 checksum.
34
34
  *
35
35
  * Accepts signature in either hex or base64 encoding.
36
- * The publisher signs the hex-encoded checksum with their
37
- * Ed25519 private key. We verify using the public key stored
38
- * in the release metadata.
36
+ * Tolerates signatures made over checksum with trailing newline
37
+ * (common when signing via `echo checksum > file && openssl sign`).
39
38
  */
40
39
  function verifyEd25519Signature(checksum, signature, publicKeyBase64) {
41
40
  try {
@@ -45,7 +44,11 @@ function verifyEd25519Signature(checksum, signature, publicKeyBase64) {
45
44
  format: "der",
46
45
  });
47
46
  const sigBuffer = decodeSignature(signature);
48
- return crypto_1.default.verify(null, Buffer.from(checksum), publicKey, sigBuffer);
47
+ if (crypto_1.default.verify(null, Buffer.from(checksum), publicKey, sigBuffer)) {
48
+ return true;
49
+ }
50
+ // Tolerate trailing newline from shell-based signing
51
+ return crypto_1.default.verify(null, Buffer.from(checksum + "\n"), publicKey, sigBuffer);
49
52
  }
50
53
  catch {
51
54
  return false;