json-type-cli
Version:
High-performance JSON Pointer implementation
144 lines (143 loc) • 5.83 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cli = void 0;
const node_util_1 = require("node:util");
const TypeSystem_1 = require("@jsonjoy.com/json-type/lib/system/TypeSystem");
const ObjectValueCaller_1 = require("@jsonjoy.com/reactive-rpc/lib/common/rpc/caller/ObjectValueCaller");
const bufferToUint8Array_1 = require("@jsonjoy.com/util/lib/buffers/bufferToUint8Array");
const util_1 = require("./util");
const methods_1 = require("./methods");
const defaultParams_1 = require("./defaultParams");
const ObjectValue_1 = require("@jsonjoy.com/json-type/lib/value/ObjectValue");
class Cli {
constructor(options) {
this.options = options;
this.paramInstances = [];
let router = options.router ?? ObjectValue_1.ObjectValue.create(new TypeSystem_1.TypeSystem());
router = (0, methods_1.defineBuiltinRoutes)(router);
this.router = router;
this.params = options.params ?? defaultParams_1.defaultParams;
this.paramMap = new Map();
for (const param of this.params) {
this.paramMap.set(param.param, param);
if (param.short)
this.paramMap.set(param.short, param);
}
this.caller = new ObjectValueCaller_1.ObjectValueCaller({ router, wrapInternalError: (err) => err });
this.types = router.system;
this.t = this.types.t;
this.codecs = options.codecs;
this.requestCodec = this.codecs.get(this.codecs.defaultCodec);
this.responseCodec = this.codecs.get(this.codecs.defaultCodec);
this.argv = options.argv ?? process.argv.slice(2);
this.stdin = options.stdin ?? process.stdin;
this.stdout = options.stdout ?? process.stdout;
this.stderr = options.stderr ?? process.stderr;
this.exit = options.exit ?? process.exit;
}
run() {
this.runAsync();
}
param(param) {
return this.paramMap.get(param);
}
async runAsync() {
try {
const system = this.router.system;
for (const key of this.router.keys())
system.alias(key, this.router.get(key).type);
const args = (0, node_util_1.parseArgs)({
args: this.argv,
strict: false,
allowPositionals: true,
});
for (let argKey of Object.keys(args.values)) {
let pointer = '';
const value = args.values[argKey];
const slashIndex = argKey.indexOf('/');
if (slashIndex !== -1) {
pointer = argKey.slice(slashIndex);
argKey = argKey.slice(0, slashIndex);
}
const param = this.param(argKey);
if (!param) {
throw new Error(`Unknown parameter "${argKey}"`);
}
const instance = param.createInstance(this, pointer, value);
this.paramInstances.push(instance);
if (instance.onParam)
await instance.onParam();
}
const method = args.positionals[0];
if (!method) {
const param = this.param('help');
const instance = param?.createInstance(this, '', undefined);
instance?.onParam?.();
throw new Error('No method specified');
}
this.request = JSON.parse(args.positionals[1] || '{}');
await this.readStdin();
for (const instance of this.paramInstances)
if (instance.onStdin)
await instance.onStdin();
for (const instance of this.paramInstances)
if (instance.onRequest)
await instance.onRequest();
try {
const ctx = { cli: this };
for (const instance of this.paramInstances)
if (instance.onBeforeCall)
await instance.onBeforeCall(method, ctx);
const value = await this.caller.call(method, this.request, ctx);
this.response = value.data;
for (const instance of this.paramInstances)
if (instance.onResponse)
await instance.onResponse();
const buf = this.responseCodec.encode(this.response);
this.stdout.write(buf);
}
catch (err) {
const error = (0, util_1.formatError)(err);
const buf = this.responseCodec.encode(error);
this.stderr.write(buf);
this.exit(1);
}
}
catch (err) {
const error = (0, util_1.formatError)(err);
const buf = JSON.stringify(error, null, 4);
this.stderr.write(buf);
this.exit(1);
}
}
cmd() {
return this.options.cmd ?? '<cmd>';
}
async getStdin() {
const stdin = this.stdin;
if (stdin.isTTY)
return Buffer.alloc(0);
const result = [];
let length = 0;
for await (const chunk of stdin) {
result.push(chunk);
length += chunk.length;
}
return Buffer.concat(result, length);
}
async readStdin() {
const stdin = this.stdin;
const codec = this.requestCodec;
if (stdin.isTTY)
return Object.create(null);
const input = await this.getStdin();
if (codec.id === 'json') {
const str = input.toString().trim();
if (!str)
return Object.create(null);
}
this.rawStdinInput = (0, bufferToUint8Array_1.bufferToUint8Array)(input);
this.stdinInput = codec.decode(this.rawStdinInput);
}
}
exports.Cli = Cli;