Files

54 lines
2.3 KiB
JavaScript

import { connect } from 'node:net';
import { BusError, PROJECT_SAVE_VERB, PROJECT_SAVE_REQUEST_MAX } from './broker.mjs';
export class Client {
constructor({ path, cap, human, tester, user, console: owner, timeout = 5000 }) {
this.path = path;
this.cap = cap;
this.human = human;
this.tester = tester;
this.user = user;
this.console = owner;
this.timeout = timeout;
}
call(verb, args = {}) {
const request =
JSON.stringify({
...(this.human ? { human: this.human } : this.tester ? { tester: this.tester } : this.user ? { user: this.user } : this.console ? { console: this.console } : { cap: this.cap }),
verb,
args,
}) + '\n';
if (Buffer.byteLength(request) > ([PROJECT_SAVE_VERB, 'library.file.save'].includes(verb) ? PROJECT_SAVE_REQUEST_MAX : 65536))
return Promise.reject(new BusError('request-too-large'));
return new Promise((resolve, reject) => {
const socket = connect(this.path);
let input = Buffer.alloc(0),
settled = false;
const end = (error, value) => {
if (settled) return;
settled = true;
socket.destroy();
error ? reject(error) : resolve(value);
};
socket.setTimeout(['processing.halt', 'processing.resume'].includes(verb) ? Math.max(this.timeout, 20000) : this.timeout, () => end(new BusError('outcome-unknown')));
socket.on('connect', () => socket.write(request));
socket.on('error', () => end(new BusError('outcome-unknown')));
socket.on('end', () => end(new BusError('outcome-unknown')));
socket.on('data', (b) => {
input = Buffer.concat([input, b]);
if (input.length > 4 * 1024 * 1024) return end(new BusError('response-too-large'));
if (!input.includes(10)) return;
try {
const r = JSON.parse(input.toString('utf8'));
if (r.ok === true) end(null, r.result);
else if (r.ok === false && typeof r.error === 'string' && /^[a-z-]{1,64}$/.test(r.error))
// Preserve a bounded reason when an explicitly scoped refusal has one.
end(new BusError(r.error, typeof r.reason === 'string' ? r.reason.slice(0, 300) : undefined));
else end(new BusError('invalid-response'));
} catch {
end(new BusError('invalid-response'));
}
});
});
}
}