@neetru/cli 2.12.2 → 2.12.5

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.
@@ -49,21 +49,24 @@ function readSchemaFile(path) {
49
49
  return fs.readFileSync(path, 'utf8');
50
50
  }
51
51
  /**
52
- * Roda `drizzle-kit generate` e captura o `.sql` gerado.
52
+ * Obtém o DDL SQL do schema via `drizzle-kit export`.
53
53
  *
54
- * Best-effort: invoca `npx drizzle-kit generate` no diretório do projeto. O
55
- * drizzle-kit escreve o `.sql` no diretório de migrations configurado; aqui
56
- * apenas capturamos stdout pra detectar falhas de processo. Quando o
57
- * `drizzle-kit` não está instalado, o `spawn` falha e o erro propaga (o
58
- * orquestrador o transforma num `PipelineError` da fase `generate`).
54
+ * BUG FIX (bug_c7842bbe): antes rodávamos `drizzle-kit generate` e retornávamos
55
+ * o STDOUT mas `generate` NÃO emite SQL no stdout: escreve o `.sql` no
56
+ * diretório `out` e imprime apenas BANNER/STATUS ("Reading config file ...",
57
+ * "N tables", ...). Esse banner virava o "SQL candidato" e quebrava o rehearse
58
+ * (o psql interpretava "No config path ..." como statement → syntax error) e,
59
+ * com `--skip-rehearse`, mandaria o banner como corpo da migração pro Core.
59
60
  *
60
- * NOTA: a leitura do `.sql` final do diretório de migrations depende da
61
- * config `drizzle.config.ts` do produtoessa resolução de path fica como
62
- * follow-up. Por ora capturamos o stdout do comando.
61
+ * `drizzle-kit export` emite o DDL PURO e AUTO-CONTIDO no STDOUT (sem banner,
62
+ * sem conexão ao banco o schema), que é exatamente o que o rehearse
63
+ * (Postgres fresco/efêmero) e o classify esperam. Verificado contra
64
+ * drizzle-kit 0.31.x. `export` lê a `drizzle.config.ts` do produto (schema +
65
+ * dialect); não precisa de `dbCredentials`.
63
66
  */
64
67
  function generateSql(schemaPath) {
65
68
  return new Promise((resolve, reject) => {
66
- const child = spawn('npx', ['drizzle-kit', 'generate'], {
69
+ const child = spawn('npx', ['drizzle-kit', 'export'], {
67
70
  cwd: process.cwd(),
68
71
  env: { ...process.env, NEETRU_DB_SCHEMA: schemaPath },
69
72
  shell: process.platform === 'win32',
@@ -77,16 +80,15 @@ function generateSql(schemaPath) {
77
80
  stderr += String(chunk);
78
81
  });
79
82
  child.on('error', (err) => {
80
- reject(new Error(`não foi possível executar drizzle-kit generate: ${err.message}`));
83
+ reject(new Error(`não foi possível executar drizzle-kit export: ${err.message}`));
81
84
  });
82
85
  child.on('exit', (code) => {
83
- if (code === 0) {
84
- resolve(stdout);
85
- }
86
- else {
87
- reject(new Error(`drizzle-kit generate falhou (exit ${code ?? '?'})` +
86
+ if (code !== 0) {
87
+ reject(new Error(`drizzle-kit export falhou (exit ${code ?? '?'})` +
88
88
  `${stderr.trim() ? `: ${stderr.trim()}` : '.'}`));
89
+ return;
89
90
  }
91
+ resolve(stdout);
90
92
  });
91
93
  });
92
94
  }
@@ -102,7 +104,15 @@ function generateSql(schemaPath) {
102
104
  * Lança `RehearseDockerUnavailableError` quando Docker indisponível — nunca
103
105
  * retorna `{ ok: true }` sem fazer o trabalho.
104
106
  */
105
- const rehearseInContainer = buildRehearseInContainer(buildDockerExecFn());
107
+ /**
108
+ * C5 (auditoria 2026-06-10): o rehearse é construído por-MOTOR. Postgres sobe
109
+ * `postgres:16-alpine` + psql; MySQL sobe `mysql:8` + cliente mysql. Antes era
110
+ * sempre Postgres, então a DDL MySQL (drizzle dialect mysql) quebrava no parser
111
+ * do Postgres e bloqueava o `neetru db apply` de engines MySQL.
112
+ */
113
+ function makeRehearse(engine) {
114
+ return buildRehearseInContainer(buildDockerExecFn(), { engine });
115
+ }
106
116
  /**
107
117
  * Classifica o `.sql` em aditiva/destrutiva via `@neetru/db-classifier`.
108
118
  * Função pura — sem I/O. O shape do `ClassificationReport` do classifier é
@@ -145,11 +155,11 @@ async function pushToCore(artifact) {
145
155
  * `overrides` permite sobrescrever deps individuais (ex.: `pushToCore` em
146
156
  * `--dry-run`, ou `onPhase` para ligar num spinner do `ora`).
147
157
  */
148
- export function buildPipelineDeps(overrides = {}) {
158
+ export function buildPipelineDeps(overrides = {}, engine = 'postgres') {
149
159
  return {
150
160
  readSchemaFile,
151
161
  generateSql,
152
- rehearseInContainer,
162
+ rehearseInContainer: makeRehearse(engine),
153
163
  classify,
154
164
  pushToCore,
155
165
  ...overrides,
@@ -1 +1 @@
1
- {"version":3,"file":"build-deps.js","sourceRoot":"","sources":["../../../src/lib/db-pipeline/build-deps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-deps.js","sourceRoot":"","sources":["../../../src/lib/db-pipeline/build-deps.ts"],"names":[],"mappings":"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"}
@@ -44,7 +44,7 @@ export interface DockerExecResult {
44
44
  * código de saída. Só lança em falha estrutural (ex.: `spawn` não encontrou o
45
45
  * executável).
46
46
  */
47
- export type DockerExecFn = (args: string[]) => Promise<DockerExecResult>;
47
+ export type DockerExecFn = (args: string[], input?: string) => Promise<DockerExecResult>;
48
48
  /** Opções para `buildRehearseInContainer`. */
49
49
  export interface RehearseOptions {
50
50
  /**
@@ -64,6 +64,14 @@ export interface RehearseOptions {
64
64
  * flag `--skip-rehearse` na CLI. Não é silencioso por contrato.
65
65
  */
66
66
  skipRehearse?: boolean;
67
+ /**
68
+ * Motor do banco a ensaiar (C5 — auditoria 2026-06-10). Default `postgres`.
69
+ * Engines MySQL (`vm-mysql-single`, `cloud-sql-mysql`) precisam ensaiar a DDL
70
+ * num MySQL real — antes o rehearse SEMPRE subia Postgres + psql, então a DDL
71
+ * MySQL (drizzle-kit dialect mysql) quebrava no parser do Postgres e bloqueava
72
+ * o apply. `mysql` sobe `mysql:8` + aplica via cliente `mysql`.
73
+ */
74
+ engine?: RehearseEngine;
67
75
  }
68
76
  /**
69
77
  * Lançado quando o Docker CLI não está disponível no PATH ou o daemon não
@@ -77,6 +85,8 @@ export declare class RehearseDockerUnavailableError extends Error {
77
85
  }
78
86
  /** Type guard — `true` sse `value` é um `RehearseDockerUnavailableError`. */
79
87
  export declare function isRehearseDockerUnavailableError(value: unknown): value is RehearseDockerUnavailableError;
88
+ /** Motor suportado pelo ensaio. */
89
+ export type RehearseEngine = 'postgres' | 'mysql';
80
90
  /**
81
91
  * Constrói a função `rehearseInContainer` com a camada Docker injetada.
82
92
  *
@@ -68,6 +68,51 @@ const PG_ENV = {
68
68
  password: 'neetru_rehearse_dev',
69
69
  db: 'neetru_rehearse',
70
70
  };
71
+ /** Credenciais do container MySQL efêmero (root, descartado em <60s). */
72
+ const MYSQL_ENV = {
73
+ password: 'neetru_rehearse_dev',
74
+ db: 'neetru_rehearse',
75
+ };
76
+ const ENGINE_SPECS = {
77
+ postgres: {
78
+ defaultImage: DEFAULT_IMAGE,
79
+ readyTimeoutMs: DEFAULT_READY_TIMEOUT_MS,
80
+ runEnvArgs: [
81
+ '-e', `POSTGRES_USER=${PG_ENV.user}`,
82
+ '-e', `POSTGRES_PASSWORD=${PG_ENV.password}`,
83
+ '-e', `POSTGRES_DB=${PG_ENV.db}`,
84
+ ],
85
+ readyCmd: (c) => [
86
+ 'exec', c, 'pg_isready', '--username', PG_ENV.user, '--dbname', PG_ENV.db, '--quiet',
87
+ ],
88
+ // psql --single-transaction: rollback total em erro parcial (container limpo).
89
+ applyCmd: (c) => [
90
+ 'exec', '--interactive', c, 'psql',
91
+ '--username', PG_ENV.user, '--dbname', PG_ENV.db,
92
+ '--no-psqlrc', '--single-transaction', '--file', '-',
93
+ ],
94
+ },
95
+ mysql: {
96
+ defaultImage: 'mysql:8',
97
+ // mysql:8 tem cold-start mais lento (entrypoint sobe servidor temporário de
98
+ // init e reinicia) — 60s cobre a janela real.
99
+ readyTimeoutMs: 60_000,
100
+ runEnvArgs: [
101
+ '-e', `MYSQL_ROOT_PASSWORD=${MYSQL_ENV.password}`,
102
+ '-e', `MYSQL_DATABASE=${MYSQL_ENV.db}`,
103
+ ],
104
+ // SELECT 1 autenticado só passa quando o servidor REAL (não o temporário de
105
+ // init) está no ar e o root é utilizável — mais robusto que `mysqladmin ping`.
106
+ readyCmd: (c) => [
107
+ 'exec', c, 'mysql', '-uroot', `-p${MYSQL_ENV.password}`, '-e', 'SELECT 1',
108
+ ],
109
+ // mysql client lê DDL do STDIN. DDL no MySQL auto-commita (não-transacional);
110
+ // o container efêmero é descartado, então não há resíduo a limpar.
111
+ applyCmd: (c) => [
112
+ 'exec', '--interactive', c, 'mysql', '-uroot', `-p${MYSQL_ENV.password}`, MYSQL_ENV.db,
113
+ ],
114
+ },
115
+ };
71
116
  // ---------------------------------------------------------------------------
72
117
  // Helpers internos
73
118
  // ---------------------------------------------------------------------------
@@ -76,19 +121,13 @@ function ephemeralContainerName() {
76
121
  return `neetru-rehearse-${randomBytes(6).toString('hex')}`;
77
122
  }
78
123
  /**
79
- * Aguarda o Postgres aceitar conexões.
80
- * Retorna `true` se ficou pronto dentro do timeout, `false` caso contrário.
124
+ * Aguarda o banco aceitar conexões, sondando `readyCmd` (pg_isready / SELECT 1)
125
+ * até exit 0. Retorna `true` se ficou pronto no timeout, `false` caso contrário.
81
126
  */
82
- async function waitForPostgresReady(execDocker, containerName, timeoutMs) {
127
+ async function waitForReady(execDocker, containerName, readyCmd, timeoutMs) {
83
128
  const deadline = Date.now() + timeoutMs;
84
129
  while (Date.now() < deadline) {
85
- const result = await execDocker([
86
- 'exec', containerName,
87
- 'pg_isready',
88
- '--username', PG_ENV.user,
89
- '--dbname', PG_ENV.db,
90
- '--quiet',
91
- ]);
130
+ const result = await execDocker(readyCmd(containerName));
92
131
  if (result.exit === 0)
93
132
  return true;
94
133
  // Aguarda antes do próximo poll.
@@ -111,8 +150,10 @@ async function waitForPostgresReady(execDocker, containerName, timeoutMs) {
111
150
  * Testes: injeta um mock de `execDocker` que controla cada resposta.
112
151
  */
113
152
  export function buildRehearseInContainer(execDocker, opts = {}) {
114
- const image = opts.image ?? DEFAULT_IMAGE;
115
- const readyTimeoutMs = opts.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
153
+ const engine = opts.engine ?? 'postgres';
154
+ const spec = ENGINE_SPECS[engine];
155
+ const image = opts.image ?? spec.defaultImage;
156
+ const readyTimeoutMs = opts.readyTimeoutMs ?? spec.readyTimeoutMs;
116
157
  const skipRehearse = opts.skipRehearse ?? false;
117
158
  return async function rehearseInContainer(sql) {
118
159
  // ── Override explícito (--skip-rehearse) ─────────────────────────────
@@ -133,9 +174,7 @@ export function buildRehearseInContainer(execDocker, opts = {}) {
133
174
  const runResult = await execDocker([
134
175
  'run', '--detach', '--rm',
135
176
  '--name', containerName,
136
- '-e', `POSTGRES_USER=${PG_ENV.user}`,
137
- '-e', `POSTGRES_PASSWORD=${PG_ENV.password}`,
138
- '-e', `POSTGRES_DB=${PG_ENV.db}`,
177
+ ...spec.runEnvArgs,
139
178
  image,
140
179
  ]);
141
180
  if (runResult.exit !== 0) {
@@ -144,26 +183,20 @@ export function buildRehearseInContainer(execDocker, opts = {}) {
144
183
  }
145
184
  // ── 3. Aguardar pronto + aplicar SQL + derrubar ───────────────────────
146
185
  try {
147
- const ready = await waitForPostgresReady(execDocker, containerName, readyTimeoutMs);
186
+ const ready = await waitForReady(execDocker, containerName, spec.readyCmd, readyTimeoutMs);
148
187
  if (!ready) {
149
188
  return {
150
189
  ok: false,
151
- error: `Timeout (${readyTimeoutMs}ms) aguardando o Postgres ficar pronto ` +
190
+ error: `Timeout (${readyTimeoutMs}ms) aguardando o ${engine} ficar pronto ` +
152
191
  `no container "${containerName}". ` +
153
192
  `Verifique se a imagem "${image}" está disponível localmente.`,
154
193
  };
155
194
  }
156
- // Aplica o SQL candidato. `--single-transaction` garante rollback total
157
- // em caso de erro parcial o container fica limpo para o teardown.
158
- const applyResult = await execDocker([
159
- 'exec', '--interactive', containerName,
160
- 'psql',
161
- '--username', PG_ENV.user,
162
- '--dbname', PG_ENV.db,
163
- '--no-psqlrc',
164
- '--single-transaction',
165
- '--command', sql,
166
- ]);
195
+ // Aplica o SQL candidato via STDIN (psql `--file -` / mysql lê stdin), NÃO
196
+ // via argumento. BUG FIX (bug_c7842bbe): SQL multi-linha como `--command`
197
+ // sofria word-split do shell no Windows → "syntax error". Pelo STDIN a SQL
198
+ // chega intacta. Comando exato por motor em ENGINE_SPECS.applyCmd.
199
+ const applyResult = await execDocker(spec.applyCmd(containerName), sql);
167
200
  if (applyResult.exit !== 0) {
168
201
  // SQL inválido — retorna o erro (não lança). O pipeline trata como
169
202
  // `PipelineError('rehearse')`.
@@ -196,11 +229,13 @@ export function buildRehearseInContainer(execDocker, opts = {}) {
196
229
  * (b) o `spawn` seja injetável em camada superior quando necessário.
197
230
  */
198
231
  export function buildDockerExecFn() {
199
- return function execDocker(args) {
232
+ return function execDocker(args, input) {
200
233
  return new Promise((resolve) => {
201
- const child = spawn('docker', args, {
202
- shell: process.platform === 'win32',
203
- });
234
+ // SEM `shell` — `docker` é um executável (docker.exe no Windows). Evitar o
235
+ // shell (a) impede word-split de argumentos (a SQL/banner vinham quebrados
236
+ // no Windows com shell:true) e (b) elimina o aviso DEP0190 (args + shell).
237
+ // Se o docker não resolver, o handler 'error' resolve exit=1 (fail-closed).
238
+ const child = spawn('docker', args);
204
239
  let stdout = '';
205
240
  let stderr = '';
206
241
  child.stdout?.on('data', (chunk) => { stdout += String(chunk); });
@@ -213,6 +248,12 @@ export function buildDockerExecFn() {
213
248
  // Combina stdout + stderr: psql escreve erros em stderr.
214
249
  resolve({ stdout: stdout + stderr, exit: code ?? 1 });
215
250
  });
251
+ // Passa a SQL via STDIN quando fornecida (psql --file -). Sempre fecha o
252
+ // stdin pra processos que não leem (version/run/stop) não travarem.
253
+ if (input !== undefined) {
254
+ child.stdin?.write(input);
255
+ }
256
+ child.stdin?.end();
216
257
  });
217
258
  };
218
259
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rehearse.js","sourceRoot":"","sources":["../../../src/lib/db-pipeline/rehearse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rehearse.js","sourceRoot":"","sources":["../../../src/lib/db-pipeline/rehearse.ts"],"names":[],"mappings":"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"}
@@ -32,11 +32,27 @@ function cleanupHandles() {
32
32
  stopAllSpinners();
33
33
  }
34
34
  catch { /* best-effort */ }
35
- // 2. Destroy stdin (handle libuv ReadStream)
36
- try {
37
- process.stdin.destroy();
35
+ // 2. Destroy stdin (handle libuv ReadStream) — MAS só quando NÃO estamos
36
+ // dentro de um menu interativo.
37
+ //
38
+ // Por quê o guard de isRaw?
39
+ // Quando `runTerminalUi` está ativo, ele faz `stdin.setRawMode(true)`.
40
+ // Se destruirmos stdin aqui (via safeExit chamado por um sub-comando),
41
+ // o `readMenuKey()` fica pendurado para sempre porque seu listener
42
+ // `process.stdin.once('data', ...)` nunca dispara em stdin destruído.
43
+ // Resultado: o top-level `await runTerminalUi()` nunca resolve e o
44
+ // Node emite "Detected unsettled top-level await".
45
+ //
46
+ // Quando stdin.isRaw === true sabemos que estamos dentro do loop do
47
+ // menu; o menu restaura stdin no próprio finally — não precisamos
48
+ // destruir aqui. Fora do menu (CLI normal), isRaw é false/undefined e
49
+ // o destroy garante que handles libuv fechem antes do process.exit().
50
+ if (!process.stdin.isRaw) {
51
+ try {
52
+ process.stdin.destroy();
53
+ }
54
+ catch { /* já fechado */ }
38
55
  }
39
- catch { /* já fechado */ }
40
56
  // 3. M-05 fix (Opus review 2026-05-27): NÃO destruir globalAgent.
41
57
  // Antes destruíamos http.globalAgent.destroy() + https.globalAgent.destroy()
42
58
  // pra fechar keep-alive sockets — mas isso quebrava telemetria flush
@@ -1 +1 @@
1
- {"version":3,"file":"safe-exit.js","sourceRoot":"","sources":["../../src/utils/safe-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,SAAS,cAAc;IACrB,gEAAgE;IAChE,IAAI,CAAC;QAAC,eAAe,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAEtD,6CAA6C;IAC7C,IAAI,CAAC;QAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAE3D,kEAAkE;IAClE,gFAAgF;IAChF,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"safe-exit.js","sourceRoot":"","sources":["../../src/utils/safe-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,SAAS,cAAc;IACrB,gEAAgE;IAChE,IAAI,CAAC;QAAC,eAAe,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAEtD,yEAAyE;IACzE,mCAAmC;IACnC,EAAE;IACF,+BAA+B;IAC/B,0EAA0E;IAC1E,0EAA0E;IAC1E,sEAAsE;IACtE,yEAAyE;IACzE,sEAAsE;IACtE,sDAAsD;IACtD,EAAE;IACF,uEAAuE;IACvE,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7D,CAAC;IAED,kEAAkE;IAClE,gFAAgF;IAChF,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neetru/cli",
3
- "version": "2.12.2",
3
+ "version": "2.12.5",
4
4
  "type": "module",
5
5
  "description": "Neetru Developer Kit — scaffold, AI assistant, publish, build e deploy de produtos SaaS Neetru via Core (VM-based agents)",
6
6
  "keywords": [
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@anthropic-ai/sdk": "^0.96.0",
44
- "@neetru/db-classifier": "^0.1.0",
44
+ "@neetru/db-classifier": "^0.1.2",
45
45
  "chalk": "^5.4.1",
46
46
  "commander": "^12.1.0",
47
47
  "conf": "^13.0.1",