@neetru/db-classifier 0.1.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.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @neetru/db-classifier
2
+
3
+ Avaliador de severidade de migração de schema SQL. Recebe um `.sql` (o output do
4
+ `drizzle-kit generate`) e devolve um `ClassificationReport` — cada statement
5
+ marcado **aditiva** ou **destrutiva**, com motivo em PT-BR e sugestão
6
+ expand-contract.
7
+
8
+ ## Por que é um pacote próprio
9
+
10
+ A classificação roda **duas vezes** no Neetru Core DB:
11
+
12
+ 1. No **CLI** — fase 4 do pipeline `neetru db apply`, para mostrar o diff e
13
+ decidir se dispara a *pausa sagrada* (confirmação humana em migração
14
+ destrutiva).
15
+ 2. No **Core** — que **recomputa** a classificação, porque não confia na que o
16
+ cliente mandou.
17
+
18
+ Se CLI e Core classificassem com lógicas diferentes, abriria um buraco de
19
+ segurança: o CLI diria "aditiva", o dev não veria alerta, e o Core aplicaria
20
+ sem a pausa. A defesa é ser **um único pacote**, importado pelos dois lados.
21
+
22
+ ## Princípios
23
+
24
+ - **Função pura, determinística, sem I/O.** Mesmo input → mesmo output.
25
+ - **Isomórfico Node** — roda em buildtime no CLI e server-side no Core.
26
+ - **Parser AST** (`node-sql-parser`), nunca regex.
27
+ - **Fail-closed** — statement fora da allowlist é classificado `destrutiva`.
28
+
29
+ ## API (contrato)
30
+
31
+ ```ts
32
+ classifyMigration(sql: string): ClassificationReport
33
+ explainStatement(c: StatementClassification): string
34
+ ```
35
+
36
+ > **Estado: implementado (M0).** `classifyMigration` e `explainStatement` estão
37
+ > entregues, com suíte de testes vitest cobrindo aditiva/destrutiva/fail-closed.
38
+ > O contrato canônico (tipos `ClassificationReport`, allowlist `STATEMENT_RULES`)
39
+ > vem da especificação do conselho do Neetru Core DB —
40
+ > `docs/_projetos/conselho-core-db/` no repositório Neetru Core (cadeira 01-cli
41
+ > e §6.1 item 1 do relatório central).
@@ -0,0 +1,21 @@
1
+ import type { ClassificationReport, StatementClassification } from './types.js';
2
+ /**
3
+ * Classifica um unico statement bruto (texto). Fail-closed: qualquer erro de
4
+ * parse vira `destrutiva` com a regra UNKNOWN.
5
+ *
6
+ * Nota: o `astify` do node-sql-parser pode devolver um ARRAY de nos quando o
7
+ * texto contem mais de um statement. Nesse caso classificamos TODOS os nos e
8
+ * agregamos — nunca apenas o primeiro. Exportada para teste direto do caminho
9
+ * multi-AST.
10
+ */
11
+ export declare function classifyOneStatement(rawSql: string): StatementClassification;
12
+ /**
13
+ * Classifica uma migracao SQL inteira (que pode conter varios statements
14
+ * separados por `;`). Funcao pura, deterministica, sem I/O.
15
+ */
16
+ export declare function classifyMigration(sql: string): ClassificationReport;
17
+ /**
18
+ * Descricao humana de uma linha (PT-BR) de uma classificacao de statement.
19
+ * Usada pelo `neetru db diff`. Funcao pura de formatacao de string.
20
+ */
21
+ export declare function explainStatement(c: StatementClassification): string;
@@ -0,0 +1,430 @@
1
+ import pkg from 'node-sql-parser';
2
+ import { STATEMENT_RULES } from './rules.js';
3
+ import { splitSqlStatements, isEffectivelyEmpty } from './split.js';
4
+ const { Parser } = pkg;
5
+ const PARSER_OPTIONS = { database: 'postgresql' };
6
+ /**
7
+ * Sugestoes expand-contract por regra destrutiva (PT-BR).
8
+ */
9
+ const SUGGESTIONS = {
10
+ DROP_COLUMN: 'Padrao expand-contract: pare de usar a coluna no codigo e faca o deploy primeiro; remova a coluna num apply posterior.',
11
+ DROP_TABLE: 'Padrao expand-contract: garanta que nada mais le ou escreve na tabela e faca o deploy primeiro; remova a tabela num apply posterior. Considere fazer backup antes.',
12
+ ALTER_COLUMN_TYPE: 'Padrao expand-contract: crie uma coluna nova com o tipo desejado, copie os dados, migre o codigo e so depois remova a coluna antiga. Mudar o tipo in-place pode reescrever a tabela inteira e travar a aplicacao.',
13
+ ADD_COLUMN_NOT_NULL_WITHOUT_DEFAULT: 'Adicione uma clausula DEFAULT (a coluna passa a ser aditiva), ou faca em dois passos: 1) adicione a coluna como nullable e preencha os dados; 2) num apply posterior aplique o NOT NULL.',
14
+ RENAME_COLUMN: 'Padrao expand-contract: adicione a coluna nova, escreva nas duas, migre as leituras e o codigo, e so entao remova a coluna antiga. Renomear quebra qualquer codigo que ainda usa o nome antigo.',
15
+ DROP_CONSTRAINT: 'Confirme que nenhuma garantia de integridade depende dessa constraint antes de remove-la. Se for recriar com outra definicao, faca isso de forma controlada num apply dedicado.',
16
+ UNKNOWN: 'O classificador nao reconheceu este statement com seguranca. Revise manualmente: por seguranca (fail-closed) ele e tratado como destrutivo e exige confirmacao humana.',
17
+ };
18
+ /**
19
+ * Extrai o nome de coluna de um no `column_ref` do node-sql-parser.
20
+ * A forma do no varia: pode ser string direta ou `{ expr: { value } }`.
21
+ */
22
+ function columnName(col) {
23
+ if (col == null)
24
+ return null;
25
+ if (typeof col === 'string')
26
+ return col;
27
+ if (typeof col === 'object') {
28
+ const c = col;
29
+ // forma column_ref: { column: { expr: { value } } } ou { column: string }
30
+ if ('column' in c) {
31
+ const inner = c['column'];
32
+ if (typeof inner === 'string')
33
+ return inner;
34
+ if (inner && typeof inner === 'object') {
35
+ const innerObj = inner;
36
+ const expr = innerObj['expr'];
37
+ if (expr && typeof expr === 'object') {
38
+ const v = expr['value'];
39
+ if (typeof v === 'string')
40
+ return v;
41
+ }
42
+ const directVal = innerObj['value'];
43
+ if (typeof directVal === 'string')
44
+ return directVal;
45
+ }
46
+ }
47
+ // forma direta { expr: { value } }
48
+ const expr = c['expr'];
49
+ if (expr && typeof expr === 'object') {
50
+ const v = expr['value'];
51
+ if (typeof v === 'string')
52
+ return v;
53
+ }
54
+ const val = c['value'];
55
+ if (typeof val === 'string')
56
+ return val;
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * Extrai o nome da tabela de um no `table` (que pode ser array de refs ou ref).
62
+ */
63
+ function tableName(node) {
64
+ if (node == null)
65
+ return null;
66
+ const ref = Array.isArray(node) ? node[0] : node;
67
+ if (typeof ref === 'string')
68
+ return ref;
69
+ if (ref && typeof ref === 'object') {
70
+ const t = ref['table'];
71
+ if (typeof t === 'string')
72
+ return t;
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Classifica uma unica acao dentro de um `ALTER TABLE`.
78
+ */
79
+ function classifyAlterAction(action, table) {
80
+ const tbl = table ?? 'desconhecida';
81
+ const act = String(action['action'] ?? '').toLowerCase();
82
+ const resource = String(action['resource'] ?? '').toLowerCase();
83
+ const col = columnName(action['column']);
84
+ const colLabel = col ? `"${col}"` : 'desconhecida';
85
+ // ADD COLUMN ...
86
+ if (act === 'add' && resource === 'column') {
87
+ const nullable = action['nullable'];
88
+ const isNotNull = nullable != null &&
89
+ typeof nullable === 'object' &&
90
+ String(nullable['type'] ?? '')
91
+ .toLowerCase()
92
+ .includes('not null');
93
+ const hasDefault = action['default_val'] != null &&
94
+ typeof action['default_val'] === 'object';
95
+ if (isNotNull && !hasDefault) {
96
+ return {
97
+ rule: 'ADD_COLUMN_NOT_NULL_WITHOUT_DEFAULT',
98
+ reason: `Adiciona a coluna ${colLabel} na tabela "${tbl}" como NOT NULL sem DEFAULT — falha se a tabela ja tiver linhas e bloqueia o apply.`,
99
+ };
100
+ }
101
+ if (isNotNull && hasDefault) {
102
+ return {
103
+ rule: 'ADD_COLUMN_NOT_NULL_WITH_DEFAULT',
104
+ reason: `Adiciona a coluna ${colLabel} na tabela "${tbl}" como NOT NULL com DEFAULT — seguro, as linhas existentes recebem o valor padrao.`,
105
+ };
106
+ }
107
+ return {
108
+ rule: 'ADD_COLUMN_NULLABLE',
109
+ reason: `Adiciona a coluna ${colLabel} (nullable) na tabela "${tbl}" — seguro, nao afeta dados existentes.`,
110
+ };
111
+ }
112
+ // DROP COLUMN ...
113
+ if (act === 'drop' && resource === 'column') {
114
+ return {
115
+ rule: 'DROP_COLUMN',
116
+ reason: `Remove a coluna ${colLabel} da tabela "${tbl}" — perda permanente dos dados dessa coluna.`,
117
+ };
118
+ }
119
+ // DROP CONSTRAINT ...
120
+ if (act === 'drop' && resource === 'constraint') {
121
+ const constraint = action['constraint'];
122
+ const cLabel = typeof constraint === 'string' ? `"${constraint}"` : 'desconhecida';
123
+ return {
124
+ rule: 'DROP_CONSTRAINT',
125
+ reason: `Remove a constraint ${cLabel} da tabela "${tbl}" — pode abrir a porta para dados invalidos / inconsistencia de integridade.`,
126
+ };
127
+ }
128
+ // ALTER COLUMN ... TYPE ...
129
+ if (act === 'alter' && resource === 'column') {
130
+ return {
131
+ rule: 'ALTER_COLUMN_TYPE',
132
+ reason: `Altera o tipo da coluna ${colLabel} na tabela "${tbl}" — pode reescrever a tabela, travar a aplicacao e perder dados na conversao.`,
133
+ };
134
+ }
135
+ // RENAME COLUMN ... / RENAME TABLE ...
136
+ if (act === 'rename') {
137
+ if (resource === 'table') {
138
+ const newName = action['table'];
139
+ const nLabel = typeof newName === 'string' ? `"${newName}"` : 'novo nome';
140
+ return {
141
+ rule: 'RENAME_COLUMN',
142
+ reason: `Renomeia a tabela "${tbl}" para ${nLabel} — quebra todo codigo que ainda usa o nome antigo.`,
143
+ };
144
+ }
145
+ return {
146
+ rule: 'RENAME_COLUMN',
147
+ reason: `Renomeia a coluna ${colLabel} na tabela "${tbl}" — quebra todo codigo que ainda usa o nome antigo.`,
148
+ };
149
+ }
150
+ // acao de ALTER TABLE nao reconhecida -> fail-closed
151
+ return {
152
+ rule: 'UNKNOWN',
153
+ reason: `Acao de ALTER TABLE nao reconhecida (${act || 'sem acao'}/${resource || 'sem recurso'}) na tabela "${tbl}" — tratada como destrutiva por seguranca (fail-closed).`,
154
+ };
155
+ }
156
+ /**
157
+ * Classifica um statement ja parseado (um no do AST).
158
+ */
159
+ function classifyAst(ast) {
160
+ const type = String(ast['type'] ?? '').toLowerCase();
161
+ const keyword = String(ast['keyword'] ?? '').toLowerCase();
162
+ // CREATE ...
163
+ if (type === 'create') {
164
+ if (keyword === 'table') {
165
+ const tbl = tableName(ast['table']);
166
+ return finalize('CREATE_TABLE', {
167
+ rule: 'CREATE_TABLE',
168
+ reason: `Cria a tabela "${tbl ?? 'nova'}" — seguro, nenhum dado existente e afetado.`,
169
+ });
170
+ }
171
+ if (keyword === 'index') {
172
+ const concurrently = ast['concurrently'];
173
+ const isConcurrent = typeof concurrently === 'string' &&
174
+ concurrently.toLowerCase().includes('concurrently');
175
+ const idx = ast['index'];
176
+ const idxLabel = typeof idx === 'string' ? `"${idx}"` : 'novo indice';
177
+ const tbl = tableName(ast['table']);
178
+ if (isConcurrent) {
179
+ return finalize('CREATE_INDEX_CONCURRENTLY', {
180
+ rule: 'CREATE_INDEX_CONCURRENTLY',
181
+ reason: `Cria o indice ${idxLabel} em "${tbl ?? 'tabela'}" com CONCURRENTLY — seguro e sem lock de escrita na tabela.`,
182
+ });
183
+ }
184
+ return finalize('CREATE_INDEX', {
185
+ rule: 'CREATE_INDEX',
186
+ reason: `Cria o indice ${idxLabel} em "${tbl ?? 'tabela'}" — seguro (considere CONCURRENTLY para evitar lock de escrita em tabelas grandes).`,
187
+ });
188
+ }
189
+ // CREATE de outro tipo (view, schema, type, ...) -> fail-closed
190
+ return finalize('UNKNOWN', {
191
+ rule: 'UNKNOWN',
192
+ reason: `Statement CREATE ${keyword || 'de tipo desconhecido'} nao esta na allowlist do classificador — tratado como destrutivo por seguranca (fail-closed).`,
193
+ });
194
+ }
195
+ // DROP TABLE ...
196
+ if (type === 'drop') {
197
+ if (keyword === 'table') {
198
+ const tbl = tableName(ast['name']);
199
+ return finalize('DROP_TABLE', {
200
+ rule: 'DROP_TABLE',
201
+ reason: `Remove a tabela "${tbl ?? 'desconhecida'}" — perda permanente da tabela e de todos os seus dados.`,
202
+ });
203
+ }
204
+ return finalize('UNKNOWN', {
205
+ rule: 'UNKNOWN',
206
+ reason: `Statement DROP ${keyword || 'de tipo desconhecido'} nao esta na allowlist do classificador — tratado como destrutivo por seguranca (fail-closed).`,
207
+ });
208
+ }
209
+ // ALTER TABLE ... (uma ou mais acoes)
210
+ if (type === 'alter') {
211
+ const table = tableName(ast['table']);
212
+ const exprRaw = ast['expr'];
213
+ const actions = Array.isArray(exprRaw)
214
+ ? exprRaw
215
+ : exprRaw && typeof exprRaw === 'object'
216
+ ? [exprRaw]
217
+ : [];
218
+ if (actions.length === 0) {
219
+ return finalize('UNKNOWN', {
220
+ rule: 'UNKNOWN',
221
+ reason: `ALTER TABLE sem acao reconhecivel na tabela "${table ?? 'desconhecida'}" — tratado como destrutivo por seguranca (fail-closed).`,
222
+ });
223
+ }
224
+ const hits = actions.map((a) => classifyAlterAction(a, table));
225
+ // severidade do statement: destrutiva se QUALQUER acao for destrutiva
226
+ const destructive = hits.find((h) => STATEMENT_RULES[h.rule] === 'destrutiva');
227
+ if (destructive) {
228
+ if (hits.length === 1)
229
+ return finalize(destructive.rule, destructive);
230
+ const reasons = hits.map((h) => h.reason).join(' ');
231
+ return finalize('UNKNOWN', {
232
+ rule: destructive.rule,
233
+ reason: `ALTER TABLE com multiplas acoes, ao menos uma destrutiva. ${reasons}`,
234
+ });
235
+ }
236
+ // todas aditivas
237
+ if (hits.length === 1)
238
+ return finalize(hits[0].rule, hits[0]);
239
+ const reasons = hits.map((h) => h.reason).join(' ');
240
+ return finalize(hits[0].rule, {
241
+ rule: hits[0].rule,
242
+ reason: `ALTER TABLE com multiplas acoes, todas aditivas. ${reasons}`,
243
+ });
244
+ }
245
+ // Qualquer outra coisa: DML (delete/update/insert/select), truncate,
246
+ // grant, etc. -> fail-closed.
247
+ return finalize('UNKNOWN', {
248
+ rule: 'UNKNOWN',
249
+ reason: `Statement do tipo "${type || 'desconhecido'}" nao e DDL de schema reconhecido (ex.: DML como DELETE/UPDATE, TRUNCATE, GRANT) — tratado como destrutivo por seguranca (fail-closed).`,
250
+ });
251
+ }
252
+ /**
253
+ * Monta a `StatementClassification` final a partir de uma regra + motivo.
254
+ * O `sql` e preenchido depois pelo chamador.
255
+ */
256
+ function finalize(rule, hit) {
257
+ const severity = STATEMENT_RULES[rule];
258
+ const out = {
259
+ sql: '',
260
+ severity,
261
+ reason: hit.reason,
262
+ };
263
+ if (severity === 'destrutiva') {
264
+ out.suggestion = SUGGESTIONS[hit.rule] ?? SUGGESTIONS.UNKNOWN;
265
+ }
266
+ return out;
267
+ }
268
+ /**
269
+ * Deteccao leve, baseada em palavra-chave, de um RENAME COLUMN que o parser
270
+ * NAO consegue parsear (limitacao do node-sql-parser 5.x para a sintaxe
271
+ * `ALTER TABLE ... RENAME COLUMN`). So e usada para dar um `reason` melhor;
272
+ * a severidade ja seria destrutiva pelo caminho fail-closed de qualquer jeito.
273
+ */
274
+ function detectUnparseableRename(rawSql) {
275
+ const normalized = rawSql.replace(/\s+/g, ' ').trim().toUpperCase();
276
+ if (/^ALTER\s+TABLE\s+.+\bRENAME\s+COLUMN\b/.test(normalized)) {
277
+ return {
278
+ rule: 'RENAME_COLUMN',
279
+ reason: 'Renomeia uma coluna (ALTER TABLE ... RENAME COLUMN) — quebra todo codigo que ainda usa o nome antigo.',
280
+ };
281
+ }
282
+ if (/^ALTER\s+TABLE\s+.+\bRENAME\s+TO\b/.test(normalized)) {
283
+ return {
284
+ rule: 'RENAME_COLUMN',
285
+ reason: 'Renomeia uma tabela (ALTER TABLE ... RENAME TO) — quebra todo codigo que ainda usa o nome antigo.',
286
+ };
287
+ }
288
+ return null;
289
+ }
290
+ /**
291
+ * Agrega uma lista de classificacoes (uma por no de AST) numa unica
292
+ * `StatementClassification`. Fail-closed: o chunk e `destrutiva` se QUALQUER
293
+ * no for `destrutiva`, e o `reason`/`suggestion` refletem o no destrutivo.
294
+ * Nunca descarta um no: o `astify` pode devolver varios nos a partir de um
295
+ * unico chunk de texto, e perder o no destrutivo aplicaria uma migracao
296
+ * perigosa sem a pausa de confirmacao.
297
+ */
298
+ function aggregateNodeClassifications(results) {
299
+ const destructive = results.find((r) => r.severity === 'destrutiva');
300
+ if (destructive) {
301
+ if (results.length === 1) {
302
+ return {
303
+ severity: 'destrutiva',
304
+ reason: destructive.reason,
305
+ ...(destructive.suggestion
306
+ ? { suggestion: destructive.suggestion }
307
+ : {}),
308
+ };
309
+ }
310
+ const reasons = results.map((r) => r.reason).join(' ');
311
+ return {
312
+ severity: 'destrutiva',
313
+ reason: `Chunk com multiplos statements, ao menos um destrutivo. ${reasons}`,
314
+ suggestion: destructive.suggestion ?? SUGGESTIONS.UNKNOWN,
315
+ };
316
+ }
317
+ // todos aditivos
318
+ if (results.length === 1) {
319
+ return { severity: 'aditiva', reason: results[0].reason };
320
+ }
321
+ const reasons = results.map((r) => r.reason).join(' ');
322
+ return {
323
+ severity: 'aditiva',
324
+ reason: `Chunk com multiplos statements, todos aditivos. ${reasons}`,
325
+ };
326
+ }
327
+ /**
328
+ * Classifica um unico statement bruto (texto). Fail-closed: qualquer erro de
329
+ * parse vira `destrutiva` com a regra UNKNOWN.
330
+ *
331
+ * Nota: o `astify` do node-sql-parser pode devolver um ARRAY de nos quando o
332
+ * texto contem mais de um statement. Nesse caso classificamos TODOS os nos e
333
+ * agregamos — nunca apenas o primeiro. Exportada para teste direto do caminho
334
+ * multi-AST.
335
+ */
336
+ export function classifyOneStatement(rawSql) {
337
+ const sql = rawSql.trim();
338
+ const parser = new Parser();
339
+ try {
340
+ const ast = parser.astify(sql, PARSER_OPTIONS);
341
+ // astify pode devolver um ARRAY (multi-statement) ou um objeto unico.
342
+ // SEGURANCA: quando for array, TODOS os nos sao classificados e agregados
343
+ // (destrutivo se qualquer um for) — nunca apenas `ast[0]`. Classificar so
344
+ // o primeiro perderia, p.ex., um DROP TABLE escondido depois de um
345
+ // CREATE TABLE e aplicaria a migracao destrutiva sem confirmacao.
346
+ const nodes = Array.isArray(ast) ? ast : [ast];
347
+ const validNodes = nodes.filter((node) => node != null && typeof node === 'object');
348
+ if (validNodes.length === 0) {
349
+ return {
350
+ sql,
351
+ severity: 'destrutiva',
352
+ reason: 'Nao foi possivel obter um AST utilizavel para este statement — tratado como destrutivo por seguranca (fail-closed).',
353
+ suggestion: SUGGESTIONS.UNKNOWN,
354
+ };
355
+ }
356
+ // Se o astify descartou nos (array tinha entradas nao-objeto), fail-closed:
357
+ // nao da pra provar que o que sobrou e seguro.
358
+ if (validNodes.length !== nodes.length) {
359
+ return {
360
+ sql,
361
+ severity: 'destrutiva',
362
+ reason: 'O parser devolveu nos de AST nao utilizaveis neste chunk — tratado como destrutivo por seguranca (fail-closed).',
363
+ suggestion: SUGGESTIONS.UNKNOWN,
364
+ };
365
+ }
366
+ const perNode = validNodes.map((node) => classifyAst(node));
367
+ const aggregated = aggregateNodeClassifications(perNode);
368
+ return { sql, ...aggregated };
369
+ }
370
+ catch {
371
+ // Parse falhou. Antes de cair no UNKNOWN generico, tenta detectar um
372
+ // RENAME que o parser nao suporta — so para um motivo mais especifico.
373
+ const renameHit = detectUnparseableRename(sql);
374
+ if (renameHit) {
375
+ return {
376
+ sql,
377
+ severity: STATEMENT_RULES[renameHit.rule], // destrutiva
378
+ reason: renameHit.reason,
379
+ suggestion: SUGGESTIONS[renameHit.rule] ?? SUGGESTIONS.UNKNOWN,
380
+ };
381
+ }
382
+ return {
383
+ sql,
384
+ severity: 'destrutiva',
385
+ reason: 'Statement SQL nao pode ser parseado (sintaxe nao reconhecida pelo parser) — tratado como destrutivo por seguranca (fail-closed).',
386
+ suggestion: SUGGESTIONS.UNKNOWN,
387
+ };
388
+ }
389
+ }
390
+ /**
391
+ * Classifica uma migracao SQL inteira (que pode conter varios statements
392
+ * separados por `;`). Funcao pura, deterministica, sem I/O.
393
+ */
394
+ export function classifyMigration(sql) {
395
+ const input = typeof sql === 'string' ? sql : '';
396
+ // Input vazio / so espacos / so comentarios -> nada a aplicar.
397
+ if (isEffectivelyEmpty(input)) {
398
+ return {
399
+ overallSeverity: 'aditiva',
400
+ statements: [],
401
+ requiresConfirmation: false,
402
+ };
403
+ }
404
+ const rawStatements = splitSqlStatements(input).filter((s) => !isEffectivelyEmpty(s));
405
+ if (rawStatements.length === 0) {
406
+ return {
407
+ overallSeverity: 'aditiva',
408
+ statements: [],
409
+ requiresConfirmation: false,
410
+ };
411
+ }
412
+ const statements = rawStatements.map(classifyOneStatement);
413
+ const overallSeverity = statements.some((s) => s.severity === 'destrutiva')
414
+ ? 'destrutiva'
415
+ : 'aditiva';
416
+ return {
417
+ overallSeverity,
418
+ statements,
419
+ requiresConfirmation: overallSeverity === 'destrutiva',
420
+ };
421
+ }
422
+ /**
423
+ * Descricao humana de uma linha (PT-BR) de uma classificacao de statement.
424
+ * Usada pelo `neetru db diff`. Funcao pura de formatacao de string.
425
+ */
426
+ export function explainStatement(c) {
427
+ const tag = c.severity === 'destrutiva' ? 'DESTRUTIVA' : 'ADITIVA';
428
+ return `[${tag}] ${c.reason}`;
429
+ }
430
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @neetru/db-classifier
3
+ *
4
+ * Avaliador de severidade de migracao SQL — fonte unica de verdade do Neetru
5
+ * Core DB. Recebe o output do `drizzle-kit generate` e devolve um
6
+ * `ClassificationReport` marcando cada statement como `aditiva` (seguro) ou
7
+ * `destrutiva` (perda de dados / risco).
8
+ *
9
+ * Funcao pura, deterministica, sem I/O. Parser AST (`node-sql-parser`), nunca
10
+ * regex. Fail-closed: tudo que nao for reconhecido vira `destrutiva`.
11
+ */
12
+ export { classifyMigration, explainStatement } from './classify.js';
13
+ export { STATEMENT_RULES } from './rules.js';
14
+ export type { RuleKey, StatementSeverity } from './rules.js';
15
+ export type { ClassificationReport, StatementClassification, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @neetru/db-classifier
3
+ *
4
+ * Avaliador de severidade de migracao SQL — fonte unica de verdade do Neetru
5
+ * Core DB. Recebe o output do `drizzle-kit generate` e devolve um
6
+ * `ClassificationReport` marcando cada statement como `aditiva` (seguro) ou
7
+ * `destrutiva` (perda de dados / risco).
8
+ *
9
+ * Funcao pura, deterministica, sem I/O. Parser AST (`node-sql-parser`), nunca
10
+ * regex. Fail-closed: tudo que nao for reconhecido vira `destrutiva`.
11
+ */
12
+ export { classifyMigration, explainStatement } from './classify.js';
13
+ export { STATEMENT_RULES } from './rules.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tabela de regras CONGELADA do classificador de migração SQL.
3
+ *
4
+ * Esta é a fonte única de verdade da severidade. Cada chave é um padrão
5
+ * canônico de statement DDL; o valor é a severidade que o Neetru Core DB
6
+ * atribui a ele.
7
+ *
8
+ * REGRA SAGRADA — fail-closed: `UNKNOWN` é SEMPRE `destrutiva`. Qualquer
9
+ * statement que o classificador não consiga mapear com confiança a uma das
10
+ * outras chaves cai em `UNKNOWN`. Nunca relaxar isso.
11
+ */
12
+ export type StatementSeverity = 'aditiva' | 'destrutiva';
13
+ /**
14
+ * Chaves de regra reconhecidas pelo classificador.
15
+ */
16
+ export type RuleKey = 'CREATE_TABLE' | 'ADD_COLUMN_NULLABLE' | 'ADD_COLUMN_NOT_NULL_WITH_DEFAULT' | 'CREATE_INDEX_CONCURRENTLY' | 'CREATE_INDEX' | 'DROP_COLUMN' | 'DROP_TABLE' | 'ALTER_COLUMN_TYPE' | 'ADD_COLUMN_NOT_NULL_WITHOUT_DEFAULT' | 'RENAME_COLUMN' | 'DROP_CONSTRAINT' | 'UNKNOWN';
17
+ /**
18
+ * Mapa congelado chave de regra → severidade. NÃO editar sem revisão de
19
+ * segurança: o CLI e o Core dependem desta tabela ser idêntica nos dois lados.
20
+ */
21
+ export declare const STATEMENT_RULES: Readonly<Record<RuleKey, StatementSeverity>>;
package/dist/rules.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tabela de regras CONGELADA do classificador de migração SQL.
3
+ *
4
+ * Esta é a fonte única de verdade da severidade. Cada chave é um padrão
5
+ * canônico de statement DDL; o valor é a severidade que o Neetru Core DB
6
+ * atribui a ele.
7
+ *
8
+ * REGRA SAGRADA — fail-closed: `UNKNOWN` é SEMPRE `destrutiva`. Qualquer
9
+ * statement que o classificador não consiga mapear com confiança a uma das
10
+ * outras chaves cai em `UNKNOWN`. Nunca relaxar isso.
11
+ */
12
+ /**
13
+ * Mapa congelado chave de regra → severidade. NÃO editar sem revisão de
14
+ * segurança: o CLI e o Core dependem desta tabela ser idêntica nos dois lados.
15
+ */
16
+ export const STATEMENT_RULES = Object.freeze({
17
+ CREATE_TABLE: 'aditiva',
18
+ ADD_COLUMN_NULLABLE: 'aditiva',
19
+ ADD_COLUMN_NOT_NULL_WITH_DEFAULT: 'aditiva',
20
+ CREATE_INDEX_CONCURRENTLY: 'aditiva',
21
+ CREATE_INDEX: 'aditiva',
22
+ DROP_COLUMN: 'destrutiva',
23
+ DROP_TABLE: 'destrutiva',
24
+ ALTER_COLUMN_TYPE: 'destrutiva',
25
+ ADD_COLUMN_NOT_NULL_WITHOUT_DEFAULT: 'destrutiva',
26
+ RENAME_COLUMN: 'destrutiva',
27
+ DROP_CONSTRAINT: 'destrutiva',
28
+ UNKNOWN: 'destrutiva',
29
+ });
30
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,MAAM,CAAC,MAAM,CAAC;IACZ,YAAY,EAAE,SAAS;IACvB,mBAAmB,EAAE,SAAS;IAC9B,gCAAgC,EAAE,SAAS;IAC3C,yBAAyB,EAAE,SAAS;IACpC,YAAY,EAAE,SAAS;IACvB,WAAW,EAAE,YAAY;IACzB,UAAU,EAAE,YAAY;IACxB,iBAAiB,EAAE,YAAY;IAC/B,mCAAmC,EAAE,YAAY;IACjD,aAAa,EAAE,YAAY;IAC3B,eAAe,EAAE,YAAY;IAC7B,OAAO,EAAE,YAAY;CACb,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Divisor de SQL em statements individuais, preservando o texto bruto.
3
+ *
4
+ * O `node-sql-parser` consegue parsear SQL multi-statement, mas ao re-serializar
5
+ * perde a formatacao original. Para o campo `sql` de cada `StatementClassification`
6
+ * precisamos do texto EXATO que o dev escreveu, entao fazemos o split nos mesmos.
7
+ *
8
+ * O split respeita:
9
+ * - literais de string entre aspas simples (`'...'`, com `''` como escape)
10
+ * - identificadores entre aspas duplas (`"..."`)
11
+ * - dollar-quoting do Postgres (`$$...$$`, `$tag$...$tag$`)
12
+ * - comentarios de linha (`-- ...`) e de bloco (barra-estrela ... estrela-barra)
13
+ *
14
+ * Um `;` so termina um statement quando esta fora de todos esses contextos.
15
+ */
16
+ export declare function splitSqlStatements(input: string): string[];
17
+ /**
18
+ * Remove comentarios e espacos de um statement para checar se "sobra" algo
19
+ * executavel. Usado para descartar trechos so-comentario.
20
+ */
21
+ export declare function isEffectivelyEmpty(stmt: string): boolean;
package/dist/split.js ADDED
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Divisor de SQL em statements individuais, preservando o texto bruto.
3
+ *
4
+ * O `node-sql-parser` consegue parsear SQL multi-statement, mas ao re-serializar
5
+ * perde a formatacao original. Para o campo `sql` de cada `StatementClassification`
6
+ * precisamos do texto EXATO que o dev escreveu, entao fazemos o split nos mesmos.
7
+ *
8
+ * O split respeita:
9
+ * - literais de string entre aspas simples (`'...'`, com `''` como escape)
10
+ * - identificadores entre aspas duplas (`"..."`)
11
+ * - dollar-quoting do Postgres (`$$...$$`, `$tag$...$tag$`)
12
+ * - comentarios de linha (`-- ...`) e de bloco (barra-estrela ... estrela-barra)
13
+ *
14
+ * Um `;` so termina um statement quando esta fora de todos esses contextos.
15
+ */
16
+ /**
17
+ * Abertura valida de dollar-quote do Postgres: `$` + tag opcional
18
+ * (`[A-Za-z_][A-Za-z0-9_]*`) + `$`. Regex STICKY (`y`) — casa apenas na
19
+ * posicao corrente do scanner (`lastIndex`), sem alocar substrings.
20
+ *
21
+ * IMPORTANTE: parametros posicionais (`$1`, `$2`, ...) NAO casam — `1` nao e
22
+ * `[A-Za-z_]` e nao ha `$` de fechamento — logo nunca disparam o estado de
23
+ * dollar-quote. So e considerado opener quando o scanner NAO esta dentro de
24
+ * outro contexto (string, identificador, comentario ou outro dollar-quote),
25
+ * o que e garantido pela ordem dos `if`/`continue` no loop abaixo.
26
+ */
27
+ const DOLLAR_OPEN = /\$([A-Za-z_][A-Za-z0-9_]*)?\$/y;
28
+ export function splitSqlStatements(input) {
29
+ const statements = [];
30
+ let current = '';
31
+ let i = 0;
32
+ const n = input.length;
33
+ while (i < n) {
34
+ const ch = input[i];
35
+ const next = i + 1 < n ? input[i + 1] : '';
36
+ // comentario de linha
37
+ if (ch === '-' && next === '-') {
38
+ let j = i;
39
+ while (j < n && input[j] !== '\n')
40
+ j++;
41
+ current += input.slice(i, j);
42
+ i = j;
43
+ continue;
44
+ }
45
+ // comentario de bloco
46
+ if (ch === '/' && next === '*') {
47
+ let j = i + 2;
48
+ while (j < n && !(input[j] === '*' && input[j + 1] === '/'))
49
+ j++;
50
+ j = Math.min(j + 2, n);
51
+ current += input.slice(i, j);
52
+ i = j;
53
+ continue;
54
+ }
55
+ // literal de string com aspas simples
56
+ if (ch === "'") {
57
+ let j = i + 1;
58
+ while (j < n) {
59
+ if (input[j] === "'") {
60
+ if (input[j + 1] === "'") {
61
+ j += 2; // aspa escapada
62
+ continue;
63
+ }
64
+ j++; // fecha
65
+ break;
66
+ }
67
+ j++;
68
+ }
69
+ current += input.slice(i, j);
70
+ i = j;
71
+ continue;
72
+ }
73
+ // identificador com aspas duplas
74
+ if (ch === '"') {
75
+ let j = i + 1;
76
+ while (j < n) {
77
+ if (input[j] === '"') {
78
+ if (input[j + 1] === '"') {
79
+ j += 2;
80
+ continue;
81
+ }
82
+ j++;
83
+ break;
84
+ }
85
+ j++;
86
+ }
87
+ current += input.slice(i, j);
88
+ i = j;
89
+ continue;
90
+ }
91
+ // dollar-quoting: $tag$ ... $tag$ (token-boundary-aware via regex sticky).
92
+ // So entramos aqui se NAO estivermos dentro de string/identificador/
93
+ // comentario — os `if`/`continue` acima ja consumiram esses contextos.
94
+ if (ch === '$') {
95
+ // Dollar-quote so abre num limite de token — nunca quando o `$` esta
96
+ // colado a um caractere de identificador (ex.: `foo$bar$baz`, onde `$`
97
+ // e parte do identificador). PG so aceita dollar-quote apos
98
+ // espaco/delimitador/inicio de input.
99
+ const prev = i > 0 ? input[i - 1] : '';
100
+ const atBoundary = i === 0 || !/[A-Za-z0-9_$]/.test(prev);
101
+ DOLLAR_OPEN.lastIndex = i;
102
+ const tagMatch = atBoundary ? DOLLAR_OPEN.exec(input) : null;
103
+ if (tagMatch && tagMatch.index === i) {
104
+ const tag = tagMatch[0]; // ex.: "$$" ou "$body$"
105
+ // O bloco fecha no PROXIMO token $tag$ EXATAMENTE igual.
106
+ const closeIdx = input.indexOf(tag, i + tag.length);
107
+ const end = closeIdx === -1 ? n : closeIdx + tag.length;
108
+ current += input.slice(i, end);
109
+ i = end;
110
+ continue;
111
+ }
112
+ // `$` que nao forma opener valido (ex.: param posicional `$1`):
113
+ // cai no caminho normal abaixo, tratado como caractere comum.
114
+ }
115
+ // separador de statement
116
+ if (ch === ';') {
117
+ const trimmed = current.trim();
118
+ if (trimmed.length > 0)
119
+ statements.push(trimmed);
120
+ current = '';
121
+ i++;
122
+ continue;
123
+ }
124
+ current += ch;
125
+ i++;
126
+ }
127
+ const tail = current.trim();
128
+ if (tail.length > 0)
129
+ statements.push(tail);
130
+ return statements;
131
+ }
132
+ /**
133
+ * Remove comentarios e espacos de um statement para checar se "sobra" algo
134
+ * executavel. Usado para descartar trechos so-comentario.
135
+ */
136
+ export function isEffectivelyEmpty(stmt) {
137
+ const stripped = stmt
138
+ .replace(/\/\*[\s\S]*?\*\//g, '')
139
+ .replace(/--[^\n]*/g, '')
140
+ .trim();
141
+ return stripped.length === 0;
142
+ }
143
+ //# sourceMappingURL=split.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split.js","sourceRoot":"","sources":["../src/split.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { StatementSeverity } from './rules.js';
2
+ export type { StatementSeverity } from './rules.js';
3
+ /**
4
+ * Classificação de um único statement SQL.
5
+ */
6
+ export interface StatementClassification {
7
+ /** SQL bruto deste statement (trimado). */
8
+ sql: string;
9
+ /** Severidade atribuída. */
10
+ severity: StatementSeverity;
11
+ /** Motivo em PT-BR — por que recebeu essa severidade. */
12
+ reason: string;
13
+ /**
14
+ * Orientação expand-contract em PT-BR. Presente quando `severity` é
15
+ * `destrutiva`; ausente quando `aditiva`.
16
+ */
17
+ suggestion?: string;
18
+ }
19
+ /**
20
+ * Relatório completo da classificação de uma migração (que pode conter
21
+ * múltiplos statements separados por `;`).
22
+ */
23
+ export interface ClassificationReport {
24
+ /** `destrutiva` se QUALQUER statement for destrutiva; caso contrário `aditiva`. */
25
+ overallSeverity: StatementSeverity;
26
+ /** Classificação por statement, na ordem do arquivo. */
27
+ statements: StatementClassification[];
28
+ /** `true` sse `overallSeverity === 'destrutiva'` — dispara a pausa sagrada. */
29
+ requiresConfirmation: boolean;
30
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@neetru/db-classifier",
3
+ "version": "0.1.0",
4
+ "description": "Avaliador de severidade de migração SQL (aditiva/destrutiva) — fonte única de verdade do Neetru Core DB. Função pura, isomórfica Node, parser AST.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run"
21
+ },
22
+ "dependencies": {
23
+ "node-sql-parser": "^5.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.6.0",
27
+ "vitest": "^2.1.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=20"
31
+ }
32
+ }