@gzl10/osx-cli 4.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.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @gzl10/osx-cli
2
+
3
+ CLI para extraer esquemas de tablas Oracle y generar JSON compatible con Atlas.
4
+
5
+ ## Instalacion
6
+
7
+ ```bash
8
+ npm install -g @gzl10/osx-cli
9
+ ```
10
+
11
+ ## Uso
12
+
13
+ ```bash
14
+ # Una tabla
15
+ osx extract CUSTOMERS --tns PROD_DB -u USER -p PASS
16
+
17
+ # Multiples tablas
18
+ osx extract CUSTOMERS,ORDERS --tns PROD_DB -u USER -p PASS -o output.json
19
+
20
+ # Con variables de entorno
21
+ export OSX_USER=USER OSX_PASSWORD=PASS
22
+ osx extract PRODUCTS --tns PROD_DB --pretty
23
+ ```
24
+
25
+ ## Opciones
26
+
27
+ | Opcion | Descripcion |
28
+ | ------ | ----------- |
29
+ | `-t, --tns` | Alias TNS (requerido) |
30
+ | `-s, --schema` | Schema Oracle (default: TNS) |
31
+ | `-u, --user` | Usuario (o env: OSX_USER) |
32
+ | `-p, --password` | Password (o env: OSX_PASSWORD) |
33
+ | `-o, --output` | Fichero salida (default: stdout) |
34
+ | `--pretty` | JSON formateado |
35
+ | `-d, --delay` | Delay entre tablas (ms) |
36
+
37
+ ## Requisitos
38
+
39
+ - Node.js >= 20
40
+ - Oracle Instant Client
41
+ - Oracle Database 11g, 12c, 18c, 19c, 21c o 23ai
42
+
43
+ ## tnsnames.ora
44
+
45
+ ```text
46
+ PROD_DB =
47
+ (DESCRIPTION =
48
+ (ADDRESS = (PROTOCOL = TCP)(HOST = oracle.example.com)(PORT = 1521))
49
+ (CONNECT_DATA = (SERVICE_NAME = ORCL))
50
+ )
51
+ ```
52
+
53
+ Variable de entorno: `TNS_ADMIN=/path/to/tnsnames`
54
+
55
+ ## Licencia
56
+
57
+ MIT
@@ -0,0 +1,137 @@
1
+ // src/services/OracleService.ts
2
+ import oracledb from "oracledb";
3
+ var OracleService = class {
4
+ connection = null;
5
+ /**
6
+ * Conecta a Oracle usando TNS
7
+ */
8
+ async connect(config) {
9
+ this.connection = await oracledb.getConnection({
10
+ user: config.user,
11
+ password: config.password,
12
+ connectString: config.tns
13
+ });
14
+ }
15
+ /**
16
+ * Ejecuta una query con parámetros nombrados
17
+ */
18
+ async query(sql, params) {
19
+ if (!this.connection) {
20
+ throw new Error(
21
+ "No hay conexi\xF3n activa a Oracle. Llama a connect() antes de ejecutar queries."
22
+ );
23
+ }
24
+ const result = await this.connection.execute(sql, params, {
25
+ outFormat: oracledb.OUT_FORMAT_OBJECT
26
+ });
27
+ return result.rows || [];
28
+ }
29
+ /**
30
+ * Cierra la conexión
31
+ */
32
+ async close() {
33
+ if (this.connection) {
34
+ await this.connection.close();
35
+ this.connection = null;
36
+ }
37
+ }
38
+ };
39
+
40
+ // src/queries/schema.ts
41
+ var TABLE_COMMENT = `
42
+ SELECT comments
43
+ FROM ALL_TAB_COMMENTS
44
+ WHERE owner = :schema AND table_name = :tableName
45
+ `;
46
+ var TABLE_COLUMNS = `
47
+ SELECT
48
+ COLS.COLUMN_NAME as "column_name",
49
+ DESCRIP.COMMENTS as "comment",
50
+ COLS.DATA_TYPE as "data_type",
51
+ COLS.DATA_LENGTH as "data_length",
52
+ COLS.DATA_PRECISION as "data_precision",
53
+ COLS.NULLABLE as "nullable",
54
+ CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'Y' ELSE 'N' END AS "is_pk"
55
+ FROM SYS.ALL_TAB_COLUMNS COLS
56
+ JOIN SYS.ALL_COL_COMMENTS DESCRIP
57
+ ON DESCRIP.OWNER = COLS.OWNER
58
+ AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME
59
+ AND DESCRIP.COLUMN_NAME = COLS.COLUMN_NAME
60
+ LEFT JOIN (
61
+ SELECT acc.COLUMN_NAME
62
+ FROM ALL_CONSTRAINTS ac
63
+ JOIN ALL_CONS_COLUMNS acc
64
+ ON ac.OWNER = acc.OWNER
65
+ AND ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
66
+ WHERE ac.CONSTRAINT_TYPE = 'P'
67
+ AND ac.OWNER = :schema
68
+ AND acc.TABLE_NAME = :tableName
69
+ ) PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME
70
+ WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName
71
+ ORDER BY COLS.COLUMN_ID
72
+ `;
73
+ var TABLE_CHILDREN = `
74
+ SELECT DISTINCT ac_child.table_name AS "child_table"
75
+ FROM all_constraints ac_parent
76
+ JOIN all_constraints ac_child
77
+ ON ac_parent.constraint_name = ac_child.r_constraint_name
78
+ AND ac_parent.owner = ac_child.r_owner
79
+ WHERE ac_parent.owner = :schema
80
+ AND ac_parent.table_name = :tableName
81
+ AND ac_parent.constraint_type = 'P'
82
+ AND ac_child.constraint_type = 'R'
83
+ AND ac_child.status = 'ENABLED'
84
+ `;
85
+
86
+ // src/services/SchemaExtractor.ts
87
+ var SchemaExtractor = class {
88
+ constructor(oracle) {
89
+ this.oracle = oracle;
90
+ }
91
+ /**
92
+ * Extrae el esquema completo de una tabla
93
+ */
94
+ async extractTable(schema, tableName) {
95
+ const [commentRow] = await this.oracle.query(TABLE_COMMENT, {
96
+ schema,
97
+ tableName
98
+ });
99
+ const columns = await this.oracle.query(TABLE_COLUMNS, {
100
+ schema,
101
+ tableName
102
+ });
103
+ const childrenRows = await this.oracle.query(TABLE_CHILDREN, {
104
+ schema,
105
+ tableName
106
+ });
107
+ return {
108
+ schema,
109
+ name: tableName,
110
+ comment: commentRow?.COMMENTS || "",
111
+ columns,
112
+ children: childrenRows.map((r) => r.child_table)
113
+ };
114
+ }
115
+ /**
116
+ * Extrae múltiples tablas con delay opcional entre ellas
117
+ */
118
+ async extractTables(schema, tableNames, delayMs = 0) {
119
+ const results = [];
120
+ for (let i = 0; i < tableNames.length; i++) {
121
+ const table = await this.extractTable(schema, tableNames[i]);
122
+ results.push(table);
123
+ if (delayMs > 0 && i < tableNames.length - 1) {
124
+ await new Promise((r) => setTimeout(r, delayMs));
125
+ }
126
+ }
127
+ return results;
128
+ }
129
+ };
130
+
131
+ export {
132
+ OracleService,
133
+ TABLE_COMMENT,
134
+ TABLE_COLUMNS,
135
+ TABLE_CHILDREN,
136
+ SchemaExtractor
137
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ OracleService,
4
+ SchemaExtractor
5
+ } from "./chunk-ILUPEXRP.js";
6
+
7
+ // src/cli.ts
8
+ import { program } from "commander";
9
+
10
+ // src/commands/extract.ts
11
+ import { writeFileSync } from "fs";
12
+ function formatOracleError(message, tns, schema) {
13
+ if (message.includes("ORA-01017")) {
14
+ return `
15
+ \u274C Error de autenticaci\xF3n en Oracle
16
+
17
+ TNS: ${tns}
18
+ Schema: ${schema}
19
+
20
+ El usuario o password son incorrectos.
21
+ Verifica las credenciales e intenta de nuevo.
22
+ `;
23
+ }
24
+ if (message.includes("ORA-12154") || message.includes("TNS:could not resolve")) {
25
+ return `
26
+ \u274C Error: No se puede resolver el alias TNS
27
+
28
+ TNS especificado: ${tns}
29
+
30
+ Posibles causas:
31
+ \u2022 El alias no existe en tnsnames.ora
32
+ \u2022 La variable TNS_ADMIN no apunta al directorio correcto
33
+ \u2022 El archivo tnsnames.ora tiene errores de sintaxis
34
+
35
+ Verifica con: cat $TNS_ADMIN/tnsnames.ora | grep -i ` + tns + "\n";
36
+ }
37
+ if (message.includes("ORA-12541") || message.includes("TNS:no listener")) {
38
+ return `
39
+ \u274C Error: No hay listener en el servidor Oracle
40
+
41
+ TNS: ${tns}
42
+
43
+ El servidor Oracle no est\xE1 escuchando conexiones.
44
+ Contacta al administrador de base de datos.
45
+ `;
46
+ }
47
+ if (message.includes("ORA-12170") || message.includes("TNS:Connect timeout")) {
48
+ return `
49
+ \u274C Error: Timeout de conexi\xF3n a Oracle
50
+
51
+ TNS: ${tns}
52
+
53
+ No se pudo establecer conexi\xF3n en el tiempo l\xEDmite.
54
+ Posibles causas:
55
+ \u2022 El servidor est\xE1 apagado o inaccesible
56
+ \u2022 Firewall bloqueando el puerto 1521
57
+ \u2022 Problemas de red
58
+ `;
59
+ }
60
+ if (message.includes("ORA-00942")) {
61
+ return `
62
+ \u274C Error: Tabla o vista no existe
63
+
64
+ Schema: ${schema}
65
+
66
+ La tabla especificada no existe o no tienes permisos.
67
+ Verifica el nombre de la tabla y los permisos del usuario.
68
+ `;
69
+ }
70
+ if (message.includes("ORA-01031")) {
71
+ return `
72
+ \u274C Error: Permisos insuficientes
73
+
74
+ Schema: ${schema}
75
+
76
+ El usuario no tiene permisos para acceder a las vistas del diccionario.
77
+ Solicita al DBA: GRANT SELECT ON SYS.ALL_TAB_COLUMNS TO ` + schema + ";\n";
78
+ }
79
+ return `
80
+ \u274C Error de Oracle
81
+
82
+ ${message}
83
+ `;
84
+ }
85
+ async function extractCommand(tables, options) {
86
+ const user = options.user || process.env.OSX_USER;
87
+ const password = options.password || process.env.OSX_PASSWORD;
88
+ const schema = options.schema || options.tns;
89
+ if (!user || !password) {
90
+ console.error(
91
+ "\n\u274C Error: Credenciales de Oracle no proporcionadas\n\nDebes especificar usuario y password de una de estas formas:\n\n 1. Flags de l\xEDnea de comandos:\n osx extract D0001 --tns TRON_LD -u USUARIO -p PASSWORD\n\n 2. Variables de entorno:\n export OSX_USER=USUARIO\n export OSX_PASSWORD=PASSWORD\n osx extract D0001 --tns TRON_LD\n"
92
+ );
93
+ process.exit(1);
94
+ }
95
+ const oracle = new OracleService();
96
+ try {
97
+ console.error(`Conectando a ${options.tns}...`);
98
+ await oracle.connect({ tns: options.tns, user, password });
99
+ const tableNames = tables.split(",").map((t) => t.trim().toUpperCase()).filter(Boolean);
100
+ if (tableNames.length === 0) {
101
+ console.error(
102
+ "\n\u274C Error: No se especificaron tablas a extraer\n\nUso: osx extract <TABLA> [--tns ALIAS]\n\nEjemplos:\n osx extract D0001 --tns TRON_LD\n osx extract D0001,D0002,D0003 --tns TRON_LD\n"
103
+ );
104
+ process.exit(1);
105
+ }
106
+ console.error(`Extrayendo ${tableNames.length} tabla(s) de ${schema}...`);
107
+ const extractor = new SchemaExtractor(oracle);
108
+ const delayMs = options.delay ? parseInt(options.delay, 10) : 0;
109
+ const results = await extractor.extractTables(schema, tableNames, delayMs);
110
+ const output = results.length === 1 ? results[0] : results;
111
+ const json = options.pretty ? JSON.stringify(output, null, 2) : JSON.stringify(output);
112
+ if (options.output) {
113
+ writeFileSync(options.output, json, "utf-8");
114
+ console.error(`Escrito: ${options.output}`);
115
+ } else {
116
+ console.log(json);
117
+ }
118
+ console.error("Completado.");
119
+ } catch (error) {
120
+ const message = error instanceof Error ? error.message : String(error);
121
+ console.error(formatOracleError(message, options.tns, schema));
122
+ process.exit(1);
123
+ } finally {
124
+ await oracle.close();
125
+ }
126
+ }
127
+
128
+ // src/cli.ts
129
+ program.name("osx").description("Oracle Schema Extractor - Genera JSON compatible con Atlas OriginType=BBDD").version("1.0.0");
130
+ program.command("extract <tables>").description("Extrae esquema de tabla(s) Oracle. Tablas separadas por coma.").requiredOption("-t, --tns <alias>", "Alias TNS de tnsnames.ora").option("-s, --schema <name>", "Schema Oracle (default: mismo que TNS)").option("-u, --user <user>", "Usuario Oracle (o env: OSX_USER)").option("-p, --password <pass>", "Password Oracle (o env: OSX_PASSWORD)").option("-o, --output <file>", "Fichero de salida (default: stdout)").option("--pretty", "JSON formateado con indentacion").option("-d, --delay <ms>", "Delay entre tablas en ms", "0").action(extractCommand);
131
+ program.parse();
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Representa una columna de Oracle
3
+ */
4
+ interface OracleColumn {
5
+ column_name: string;
6
+ comment: string | null;
7
+ data_type: string;
8
+ data_length: number | null;
9
+ data_precision: number | null;
10
+ nullable: 'Y' | 'N';
11
+ is_pk: 'Y' | 'N';
12
+ }
13
+ /**
14
+ * Esquema de tabla compatible con Atlas OriginType=BBDD
15
+ */
16
+ interface TableSchema {
17
+ schema: string;
18
+ name: string;
19
+ comment: string;
20
+ columns: OracleColumn[];
21
+ children: string[];
22
+ }
23
+ /**
24
+ * Opciones del comando extract
25
+ */
26
+ interface ExtractOptions {
27
+ tns: string;
28
+ schema?: string;
29
+ user?: string;
30
+ password?: string;
31
+ output?: string;
32
+ pretty?: boolean;
33
+ delay?: string;
34
+ }
35
+ /**
36
+ * Configuración de conexión Oracle
37
+ */
38
+ interface OracleConfig {
39
+ tns: string;
40
+ user: string;
41
+ password: string;
42
+ }
43
+
44
+ /**
45
+ * Servicio de conexión y queries a Oracle
46
+ */
47
+ declare class OracleService {
48
+ private connection;
49
+ /**
50
+ * Conecta a Oracle usando TNS
51
+ */
52
+ connect(config: OracleConfig): Promise<void>;
53
+ /**
54
+ * Ejecuta una query con parámetros nombrados
55
+ */
56
+ query<T extends object>(sql: string, params: Record<string, unknown>): Promise<T[]>;
57
+ /**
58
+ * Cierra la conexión
59
+ */
60
+ close(): Promise<void>;
61
+ }
62
+
63
+ /**
64
+ * Extractor de esquemas de tablas Oracle
65
+ */
66
+ declare class SchemaExtractor {
67
+ private oracle;
68
+ constructor(oracle: OracleService);
69
+ /**
70
+ * Extrae el esquema completo de una tabla
71
+ */
72
+ extractTable(schema: string, tableName: string): Promise<TableSchema>;
73
+ /**
74
+ * Extrae múltiples tablas con delay opcional entre ellas
75
+ */
76
+ extractTables(schema: string, tableNames: string[], delayMs?: number): Promise<TableSchema[]>;
77
+ }
78
+
79
+ /**
80
+ * Query para obtener el comentario de una tabla
81
+ */
82
+ declare const TABLE_COMMENT = "\n SELECT comments\n FROM ALL_TAB_COMMENTS\n WHERE owner = :schema AND table_name = :tableName\n";
83
+ /**
84
+ * Query para obtener las columnas de una tabla con sus metadatos
85
+ * Incluye: nombre, comentario, tipo de dato, nullable, si es PK
86
+ */
87
+ declare const TABLE_COLUMNS = "\n SELECT\n COLS.COLUMN_NAME as \"column_name\",\n DESCRIP.COMMENTS as \"comment\",\n COLS.DATA_TYPE as \"data_type\",\n COLS.DATA_LENGTH as \"data_length\",\n COLS.DATA_PRECISION as \"data_precision\",\n COLS.NULLABLE as \"nullable\",\n CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'Y' ELSE 'N' END AS \"is_pk\"\n FROM SYS.ALL_TAB_COLUMNS COLS\n JOIN SYS.ALL_COL_COMMENTS DESCRIP\n ON DESCRIP.OWNER = COLS.OWNER\n AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME\n AND DESCRIP.COLUMN_NAME = COLS.COLUMN_NAME\n LEFT JOIN (\n SELECT acc.COLUMN_NAME\n FROM ALL_CONSTRAINTS ac\n JOIN ALL_CONS_COLUMNS acc\n ON ac.OWNER = acc.OWNER\n AND ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME\n WHERE ac.CONSTRAINT_TYPE = 'P'\n AND ac.OWNER = :schema\n AND acc.TABLE_NAME = :tableName\n ) PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME\n WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName\n ORDER BY COLS.COLUMN_ID\n";
88
+ /**
89
+ * Query para obtener las tablas hijas (que tienen FK hacia esta tabla)
90
+ */
91
+ declare const TABLE_CHILDREN = "\n SELECT DISTINCT ac_child.table_name AS \"child_table\"\n FROM all_constraints ac_parent\n JOIN all_constraints ac_child\n ON ac_parent.constraint_name = ac_child.r_constraint_name\n AND ac_parent.owner = ac_child.r_owner\n WHERE ac_parent.owner = :schema\n AND ac_parent.table_name = :tableName\n AND ac_parent.constraint_type = 'P'\n AND ac_child.constraint_type = 'R'\n AND ac_child.status = 'ENABLED'\n";
92
+
93
+ export { type ExtractOptions, type OracleColumn, type OracleConfig, OracleService, SchemaExtractor, TABLE_CHILDREN, TABLE_COLUMNS, TABLE_COMMENT, type TableSchema };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import {
2
+ OracleService,
3
+ SchemaExtractor,
4
+ TABLE_CHILDREN,
5
+ TABLE_COLUMNS,
6
+ TABLE_COMMENT
7
+ } from "./chunk-ILUPEXRP.js";
8
+ export {
9
+ OracleService,
10
+ SchemaExtractor,
11
+ TABLE_CHILDREN,
12
+ TABLE_COLUMNS,
13
+ TABLE_COMMENT
14
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@gzl10/osx-cli",
3
+ "version": "4.0.0",
4
+ "description": "Oracle Schema Extractor CLI - Genera JSON compatible con Atlas OriginType=BBDD",
5
+ "type": "module",
6
+ "bin": {
7
+ "osx": "./dist/cli.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "oracle",
20
+ "schema",
21
+ "extractor",
22
+ "cli",
23
+ "atlas",
24
+ "database"
25
+ ],
26
+ "author": "GZL10",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "commander": "^13.1.0",
30
+ "oracledb": "^6.7.1",
31
+ "zod": "^3.24.1"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.10.5",
35
+ "@vitest/coverage-v8": "^2.1.8",
36
+ "tsup": "^8.3.5",
37
+ "typescript": "^5.7.3",
38
+ "vitest": "^2.1.8"
39
+ },
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup && node scripts/add-shebang.js",
45
+ "dev": "tsup --watch",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "test:coverage": "vitest run --coverage"
50
+ }
51
+ }