@httpc/cli
Version:
httpc cli for building function-based API with minimal code and end-to-end type safety
151 lines (150 loc) • 5.67 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CallCommand = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const commander_1 = require("commander");
const prompts_1 = __importDefault(require("prompts"));
const kleur_1 = __importDefault(require("kleur"));
const client_1 = require("@httpc/client");
const utils_1 = require("../utils");
exports.CallCommand = (0, commander_1.createCommand)("call")
.description("call a function on an httpc server")
.option("--setup", "create a configuration used for all calls")
.option("--no-config", "don't load the config even if present")
.option("-s, --server <address>", "the httpc server address")
.addOption(new commander_1.Option("-a, --access <level>", "set the call access type").choices(["read", "write"]))
.option("--read", "shortcut for --access read")
.arguments("[arguments...]")
.configureHelp({
commandDescription: () => "",
commandUsage: cmd => `
httpc ${cmd.name()} --setup create a configuration used for all calls
httpc ${cmd.name()} <function> [arguments...] call a function on an httpc server`
})
.action(async ([callPath, ...args], options) => {
if (options.setup) {
await runCallSetup();
return;
}
if (!callPath) {
throw new Error("Missing function to call");
}
const config = options.config ? await readConfig() : undefined;
const { read, access = read ? "read" : "write", endpoint = config?.endpoint || "http://localhost:3000", } = options;
// args preprocessing
// 1. try to parse numbers
// 2. try to parse objects {...}
const params = args.map(x => {
if (!isNaN(x)) {
return Number(x);
}
else if (x.startsWith("{") && x.endsWith("}")) {
return JSON.parse(x);
}
else {
return x;
}
});
const client = createClient({
...config,
endpoint,
});
const result = await (access === "read"
? client.read(callPath, ...params)
: client.write(callPath, ...params)).catch(err => {
if (err instanceof Error && err.name === "FetchError") {
utils_1.log.error(err.message);
return err;
}
if (err instanceof client_1.HttpCClientError) {
utils_1.log.error(`HTTP ${err.status}`);
return err.body;
}
throw err;
});
if (result instanceof Error) {
// already handled
return;
}
if (result === undefined) {
utils_1.log.minor("(no value)");
return;
}
console.log(JSON.stringify(result, undefined, 2));
});
const DEFAULT_CONFIG_FILE = "httpc-client.config.local";
async function readConfig() {
try {
const configFile = DEFAULT_CONFIG_FILE;
return JSON.parse(await promises_1.default.readFile(configFile, "utf8"));
}
catch {
}
}
async function runCallSetup() {
console.log("This procedure will help to create a common configuration used by all calls");
const responses = await (0, prompts_1.default)([
{
name: "endpoint",
type: "text",
message: "Type the server address",
initial: "http://localhost:3000",
validate: value => {
try {
return !!new URL(value);
}
catch {
return false;
}
}
},
{
name: "authentication",
type: "select",
message: "Select authentication",
choices: [
{ title: "None", value: "none" },
{ title: "Bearer", value: "Bearer" },
{ title: "ApiKey", value: "ApiKey" },
]
},
{
name: "token",
type: prev => prev === "none" ? null : "password",
message: prev => `Type the ${prev === "Bearer" ? "bearer token" : "api key"}`
},
], {
onCancel: () => {
process.exit(0);
}
});
const configuration = {
endpoint: responses.endpoint,
authentication: responses.authentication !== "none" ? `${responses.authentication} ${responses.token}` : undefined
};
const configFile = path_1.default.resolve(DEFAULT_CONFIG_FILE);
await promises_1.default.writeFile(configFile, JSON.stringify(configuration, undefined, 2), "utf8");
console.log("\r");
utils_1.log.done(`Written configuration to: ${kleur_1.default.bold("%s")}`, configFile);
console.log("\nNow on this configuration is shared by all calls");
console.log(`You can run ${kleur_1.default.bold("httpc call --setup")} again to change it`);
console.log("Or you can delete it if you don't need it anymore");
}
function createClient(options) {
const client = new client_1.HttpCClient({
endpoint: options.endpoint,
});
if (options.authentication) {
const [schema, value] = options.authentication.split(" ");
if (!schema || !value) {
utils_1.log.error("Invalid configuration: authentication is malformed");
process.exit(1);
}
client.use((0, client_1.AuthHeader)(schema, value));
}
return client;
}
;