@foxglove/xmlrpc
Version:
TypeScript library implementing an XMLRPC client and server with pluggable server backend
180 lines • 6.64 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeFault = exports.serializeMethodResponse = exports.serializeMethodCall = exports.XmlRpcError = void 0;
const byte_base64_1 = require("byte-base64");
const xmlbuilder2_1 = require("xmlbuilder2");
const CustomType_1 = require("./CustomType");
const DateFormatter_1 = require("./DateFormatter");
const illegalChars = /^(?![^<&]*]]>[^<&]*)[^<&]*$/;
const dateFormatter = new DateFormatter_1.DateFormatter();
// ref <http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php>
var XmlRpcError;
(function (XmlRpcError) {
XmlRpcError[XmlRpcError["APPLICATION_ERROR"] = -32500] = "APPLICATION_ERROR";
XmlRpcError[XmlRpcError["NOT_FOUND_ERROR"] = -32601] = "NOT_FOUND_ERROR";
XmlRpcError[XmlRpcError["INVALID_PARAMS_ERROR"] = -32602] = "INVALID_PARAMS_ERROR";
})(XmlRpcError = exports.XmlRpcError || (exports.XmlRpcError = {}));
// Creates the XML for an XML-RPC method call
function serializeMethodCall(method, params = [], encoding) {
const xml = (0, xmlbuilder2_1.create)({ version: "1.0", encoding })
.ele("methodCall")
.ele("methodName")
.txt(method)
.up()
.ele("params");
params.forEach((param) => serializeValue(param, xml.ele("param")));
// Includes the <?xml ...> declaration
return xml.doc().toString();
}
exports.serializeMethodCall = serializeMethodCall;
// Creates the XML for an XML-RPC method response
function serializeMethodResponse(result) {
const xml = (0, xmlbuilder2_1.create)().ele("methodResponse").ele("params").ele("param");
serializeValue(result, xml);
// Includes the <?xml ...> declaration
return xml.doc().toString();
}
exports.serializeMethodResponse = serializeMethodResponse;
function serializeFault(fault) {
const xml = (0, xmlbuilder2_1.create)().ele("methodResponse").ele("fault");
const faultCode = fault.faultCode ?? XmlRpcError.APPLICATION_ERROR;
const faultString = fault.faultString ?? fault.message;
serializeValue({ faultCode, faultString }, xml);
// Includes the <?xml ...> declaration
return xml.doc().toString();
}
exports.serializeFault = serializeFault;
function serializeValue(value, xml) {
let current = { value, xml };
const stack = [current];
let valueNode;
let next;
while (stack.length > 0) {
current = stack[stack.length - 1];
if (current.index != undefined) {
// Iterating a compound
next = getNextItemsFrame(current);
if (next != undefined) {
stack.push(next);
}
else {
stack.pop();
}
}
else {
// we're about to add a new value (compound or simple)
valueNode = current.xml.ele("value");
switch (typeof current.value) {
case "boolean":
appendBoolean(current.value, valueNode);
stack.pop();
break;
case "string":
appendString(current.value, valueNode);
stack.pop();
break;
case "number":
appendNumber(current.value, valueNode);
stack.pop();
break;
case "object":
if (current.value == undefined) {
valueNode.ele("nil");
stack.pop();
}
else if (current.value instanceof Date) {
appendDatetime(current.value, valueNode);
stack.pop();
}
else if (Buffer.isBuffer(current.value)) {
appendBuffer(current.value, valueNode);
stack.pop();
}
else if (current.value instanceof CustomType_1.CustomType) {
current.value.serialize(valueNode);
stack.pop();
}
else {
if (Array.isArray(current.value)) {
current.xml = valueNode.ele("array").ele("data");
}
else {
current.xml = valueNode.ele("struct");
current.keys = Object.keys(current.value);
}
current.index = 0;
next = getNextItemsFrame(current);
if (next != undefined) {
stack.push(next);
}
else {
stack.pop();
}
}
break;
default:
stack.pop();
break;
}
}
}
}
function getNextItemsFrame(frame) {
let nextFrame;
if (frame.keys != undefined && frame.index != undefined) {
if (frame.index < frame.keys.length) {
const key = frame.keys[frame.index++];
const member = frame.xml.ele("member").ele("name").txt(key).up();
nextFrame = {
value: frame.value[key],
xml: member,
};
}
}
else if (frame.index != undefined &&
Array.isArray(frame.value) &&
frame.index < frame.value.length) {
nextFrame = {
value: frame.value[frame.index],
xml: frame.xml,
};
frame.index++;
}
return nextFrame;
}
// eslint-disable-next-line @foxglove/no-boolean-parameters
function appendBoolean(value, xml) {
xml.ele("boolean").txt(value ? "1" : "0");
}
function appendString(value, xml) {
if (!illegalChars.test(value)) {
xml.ele("string").dat(value);
}
else {
xml.ele("string").txt(value);
}
}
function appendNumber(value, xml) {
if (value % 1 === 0) {
xml.ele("int").txt(String(value));
}
else if (value === Infinity) {
xml.ele("double").txt("inf");
}
else if (value === -Infinity) {
xml.ele("double").txt("-inf");
}
else if (isNaN(value)) {
xml.ele("double").txt("nan");
}
else {
xml.ele("double").txt(String(value));
}
}
function appendDatetime(value, xml) {
xml.ele("dateTime.iso8601").txt(dateFormatter.encodeIso8601(value));
}
function appendBuffer(value, xml) {
xml.ele("base64").txt((0, byte_base64_1.bytesToBase64)(value));
}
//# sourceMappingURL=Serializer.js.map