Improve command-line handling

This commit is contained in:
Jan Hamal Dvořák 2025-08-26 12:39:50 +02:00
parent 6a1a83eb62
commit 3892e149be
3 changed files with 150 additions and 11 deletions

73
package-lock.json generated
View file

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"command-line-args": "^6.0.1",
"find-up": "^7.0.0",
"oracledb": "^6.8.0"
},
@ -16,6 +17,7 @@
"node-oracledb-cli": "dist/index.js"
},
"devDependencies": {
"@types/command-line-args": "^5.2.3",
"@types/oracledb": "^6.9.1",
"tsx": "^4.20.4",
"typescript": "^5.9.2"
@ -463,6 +465,13 @@
"node": ">=18"
}
},
"node_modules/@types/command-line-args": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz",
"integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
@ -483,6 +492,38 @@
"@types/node": "*"
}
},
"node_modules/array-back": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz",
"integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
"license": "MIT",
"engines": {
"node": ">=12.17"
}
},
"node_modules/command-line-args": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz",
"integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==",
"license": "MIT",
"dependencies": {
"array-back": "^6.2.2",
"find-replace": "^5.0.2",
"lodash.camelcase": "^4.3.0",
"typical": "^7.2.0"
},
"engines": {
"node": ">=12.20"
},
"peerDependencies": {
"@75lb/nature": "latest"
},
"peerDependenciesMeta": {
"@75lb/nature": {
"optional": true
}
}
},
"node_modules/esbuild": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
@ -525,6 +566,23 @@
"@esbuild/win32-x64": "0.25.9"
}
},
"node_modules/find-replace": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz",
"integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==",
"license": "MIT",
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@75lb/nature": "latest"
},
"peerDependenciesMeta": {
"@75lb/nature": {
"optional": true
}
}
},
"node_modules/find-up": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
@ -585,6 +643,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"license": "MIT"
},
"node_modules/oracledb": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/oracledb/-/oracledb-6.9.0.tgz",
@ -678,6 +742,15 @@
"node": ">=14.17"
}
},
"node_modules/typical": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz",
"integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==",
"license": "MIT",
"engines": {
"node": ">=12.17"
}
},
"node_modules/undici-types": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",

View file

@ -1,7 +1,9 @@
{
"name": "node-oracledb-cli",
"version": "1.0.0",
"description": "",
"author": "Jan Hamal Dvořák <mordae@anilinux.org>",
"license": "MIT",
"description": "Command-line Oracle Client",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
@ -10,14 +12,14 @@
"bin": {
"node-oracledb-cli": "dist/index.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/command-line-args": "^5.2.3",
"@types/oracledb": "^6.9.1",
"tsx": "^4.20.4",
"typescript": "^5.9.2"
},
"dependencies": {
"command-line-args": "^6.0.1",
"find-up": "^7.0.0",
"oracledb": "^6.8.0"
}

View file

@ -1,9 +1,11 @@
#!/usr/bin/env node
import fs from 'node:fs'
import os from 'node:os'
import readline from 'node:readline'
import oracledb from 'oracledb'
import { findUpSync } from 'find-up'
import commandLineArgs from 'command-line-args'
interface OraTable {
OWNER: string
@ -87,15 +89,73 @@ function getStringParts(str: string, offset: number) {
return { init, word, rest }
}
async function main() {
const args = process.argv.slice(2)
interface Options {
host: string
port: number
dbname: string
username: string
help: boolean
}
if (args.length < 3) {
console.log('Usage: node oracle-client.js <user> <password> <connectString>')
process.exit(1)
async function main() {
const { username } = os.userInfo()
const options = commandLineArgs([
{ name: 'host', alias: 'h', type: String, defaultValue: 'localhost' },
{ name: 'port', alias: 'p', type: parseInt, defaultValue: 1521 },
{ name: 'dbname', alias: 'd', type: String, defaultValue: 'hr', defaultOption: true },
{ name: 'username', alias: 'U', type: String, defaultValue: username },
{ name: 'help', type: Boolean, defaultValue: false },
]) as Options
if (options.help) {
console.log('Usage: node-oracledb-cli -h dbhost -U username dbname')
console.log('Command line client for Oracle SQL databases\n')
console.log('Options:')
console.log(' --help Show this help')
console.log(' --host, -h HOST Address of the host to connect to')
console.log(' --port, -p PORT Port of the host to connect to')
console.log(' --dbname, -d NAME Name of the database/service to connect to')
console.log(' --username, -u NAME Name of the user for login')
console.log('\nAuthentication:')
console.log(' You need to create .orapass file in this or a parent directory.')
console.log(' The file shall contain "host:port:dbname:username:password" lines.')
process.exit(0)
}
const [user, password, connectString] = args
const passFile = findUpSync([
'.config/node-oracledb-cli/passwords',
'.oracle_passwords',
'.orapass',
])
const passLines = passFile ? fs.readFileSync(passFile, 'utf8') : ''
const passwords = passLines
.split('\n')
.map((line) => line.trim().split(/:/g))
.filter(([h, p, d, u]) => {
if (
h === options.host &&
parseInt(p) === options.port &&
d === options.dbname &&
u === options.username
) {
return true
}
})
.map((row) => row[4])
if (process.env.ORACLE_PASSWORD !== undefined) {
passwords.unshift(process.env.ORACLE_PASSWORD)
}
if (!passwords.length) {
console.error('Password not found. Please create .orapass entry like this:\n')
console.error(
`${options.host}:${options.port}:${options.dbname}:${options.username}:Passw0rd123\n`,
)
console.error('Or set ORACLE_PASSWORD environment variable and try again.')
process.exit(1)
}
const historyFile = findUpSync(['.config/node-oracledb-cli/history', '.oracle_history'])
const historyLines = historyFile ? fs.readFileSync(historyFile, 'utf8') : ''
@ -107,7 +167,7 @@ async function main() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: `${user}@${connectString}> `,
prompt: `${options.username}@${options.dbname}> `,
removeHistoryDuplicates: true,
history,
completer(linePartial: string) {
@ -160,7 +220,11 @@ async function main() {
},
})
const connection = await oracledb.getConnection({ user, password, connectString })
const connection = await oracledb.getConnection({
user: options.username,
password: passwords[0],
connectString: `${options.host}:${options.port}/${options.dbname}`,
})
function execute<R>(query: string, args: any[] = [], maxRows?: number) {
return connection.execute<R>(query, args, {