@xapi-js/core
Version:
Core utilities and types for X-API.
806 lines (799 loc) • 25.3 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ColumnTypeError: () => ColumnTypeError,
Dataset: () => Dataset,
InvalidXmlError: () => InvalidXmlError,
NexaVersion: () => NexaVersion,
StringWritableStream: () => StringWritableStream,
XapiRoot: () => XapiRoot,
XplatformVersion: () => XplatformVersion,
_unescapeXml: () => _unescapeXml,
arrayBufferToString: () => arrayBufferToString,
base64ToUint8Array: () => base64ToUint8Array,
columnType: () => columnType,
convertToColumnType: () => convertToColumnType,
convertToString: () => convertToString,
dateToString: () => dateToString,
initXapi: () => initXapi,
makeParseEntities: () => makeParseEntities,
makeWriterEntities: () => makeWriterEntities,
parse: () => parse2,
rowType: () => rowType,
stringToDate: () => stringToDate,
stringToReadableStream: () => stringToReadableStream,
uint8ArrayToBase64: () => uint8ArrayToBase64,
write: () => write,
writeString: () => writeString
});
module.exports = __toCommonJS(index_exports);
// src/handler.ts
var import_stax_xml = require("stax-xml");
var txml = __toESM(require("txml"), 1);
// src/types.ts
var rowType = ["insert", "update", "delete"];
var columnType = ["STRING", "INT", "FLOAT", "DECIMAL", "BIGDECIMAL", "DATE", "DATETIME", "TIME", "BLOB"];
var XplatformVersion = {
xmlns: "http://www.tobesoft.com/platform/Dataset",
version: "4000"
};
var NexaVersion = {
xmlns: "http://www.nexacroplatform.com/platform/dataset",
version: "4000"
};
var ColumnTypeError = class extends Error {
constructor(message) {
super(message);
this.name = "ColumnTypeError";
}
};
var InvalidXmlError = class extends Error {
constructor(message) {
super(message);
this.name = "InvalidXmlError";
}
};
// src/utils.ts
function arrayBufferToString(buffer) {
const decoder = new TextDecoder();
return decoder.decode(buffer);
}
function makeParseEntities() {
const entities2 = [];
for (let i = 1; i <= 32; i++) {
entities2.push({ entity: `&#${i};`, value: String.fromCharCode(i) });
}
return entities2;
}
function makeWriterEntities() {
const entities2 = [];
for (let i = 1; i <= 32; i++) {
entities2.push({ entity: `&#${i};`, value: String.fromCharCode(i) });
}
return entities2;
}
function base64ToUint8Array(base64String) {
const binaryString = atob(base64String);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
function uint8ArrayToBase64(uint8Array) {
let binaryString = "";
for (let i = 0; i < uint8Array.length; i++) {
binaryString += String.fromCharCode(uint8Array[i]);
}
return btoa(binaryString);
}
function stringToDate(value) {
if (!value) return void 0;
let year;
let month;
let day;
let hours = 0;
let minutes = 0;
let seconds = 0;
let milliseconds = 0;
if (value.length < 6 || value.length > 16) {
return void 0;
}
if (value.length >= 8) {
year = parseInt(value.substring(0, 4), 10);
month = parseInt(value.substring(4, 6), 10) - 1;
day = parseInt(value.substring(6, 8), 10);
} else {
year = 1970;
month = 0;
day = 1;
}
if (value.length === 6) {
hours = parseInt(value.substring(0, 2), 10);
minutes = parseInt(value.substring(2, 4), 10);
seconds = parseInt(value.substring(4, 6), 10);
} else if (value.length >= 10) {
hours = parseInt(value.substring(8, 10), 10);
minutes = parseInt(value.substring(10, 12), 10);
seconds = parseInt(value.substring(12, 14), 10);
}
if (value.length === 16) {
milliseconds = parseInt(value.substring(14, 16), 10);
}
if (year < 1970 || year > 9999 || month < 0 || month > 11 || day < 1 || day > 31 || hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59 || milliseconds < 0 || milliseconds > 999) {
return void 0;
}
const date = new Date(year, month, day, hours, minutes, seconds, milliseconds);
if (isNaN(date.getTime())) {
return void 0;
}
return date;
}
function dateToString(date, type) {
const year = date.getFullYear().toString().padStart(4, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
const seconds = date.getSeconds().toString().padStart(2, "0");
switch (type) {
case "DATE":
return `${year}${month}${day}`;
case "DATETIME":
return `${year}${month}${day}${hours}${minutes}${seconds}`;
case "TIME":
return `${hours}${minutes}${seconds}`;
default:
return "";
}
}
var StringWritableStream = class extends WritableStream {
result = "";
constructor() {
const decoder = new TextDecoder();
super({
write: (chunk) => {
this.result += decoder.decode(chunk, { stream: true });
},
close: () => {
this.result += decoder.decode();
}
});
}
/**
* Returns the collected string result.
* @returns The concatenated string from all written chunks.
*/
getResult() {
return this.result;
}
};
function stringToReadableStream(str) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
return new ReadableStream({
start(controller) {
controller.enqueue(bytes);
controller.close();
}
});
}
function convertToColumnType(value, type) {
switch (type) {
case "INT":
case "BIGDECIMAL":
const intValue = parseInt(value, 10);
return isNaN(intValue) ? value : intValue;
case "FLOAT":
const floatValue = parseFloat(value);
return isNaN(floatValue) ? value : floatValue;
case "DECIMAL":
const decimalValue = parseFloat(value);
return isNaN(decimalValue) ? value : decimalValue;
case "DATE":
case "DATETIME":
case "TIME":
return stringToDate(value) || value;
case "BLOB":
try {
return base64ToUint8Array(value);
} catch {
return value;
}
case "STRING":
return value;
default:
throw new ColumnTypeError(`Unsupported column type: ${type}`);
}
}
function convertToString(value, type) {
switch (type) {
case "INT":
case "BIGDECIMAL":
case "FLOAT":
case "DECIMAL":
return String(value);
case "DATE":
case "DATETIME":
case "TIME":
return dateToString(value, type);
case "BLOB":
return uint8ArrayToBase64(value);
case "STRING":
return String(value);
default:
return String(value);
}
}
var entities = makeParseEntities();
function _unescapeXml(str) {
if (!str) return str;
const regex = new RegExp(entities.map((e) => e.entity).join("|"), "g");
return str.replace(regex, (match) => {
const entity = entities.find((e) => e.entity === match);
return entity ? entity.value : match;
});
}
// src/xapi-data.ts
var XapiRoot = class {
/** An array of datasets within the X-API root. */
datasets = [];
/** Parameters associated with the X-API root. */
parameters = { params: [] };
/**
* Creates an instance of XapiRoot.
* @param datasets - Initial array of datasets.
* @param parameters - Initial parameters.
*/
constructor(datasets = [], parameters = { params: [] }) {
this.datasets = datasets;
this.parameters = parameters;
}
/**
* Adds a dataset to the X-API root.
* @param dataset - The dataset to add.
*/
addDataset(dataset) {
this.datasets.push(dataset);
}
/**
* Retrieves a dataset by its ID.
* @param id - The ID of the dataset to retrieve.
* @returns The Dataset object, or undefined if not found.
*/
getDataset(id) {
return this.datasets.find((dataset) => dataset.id === id);
}
/**
* Adds a parameter to the X-API root.
* @param parameter - The parameter to add.
*/
addParameter(parameter) {
this.parameters.params.push(parameter);
}
/**
* Retrieves a parameter by its ID.
* @param id - The ID of the parameter to retrieve.
* @returns The Parameter object, or undefined if not found.
*/
getParameter(id) {
return this.parameters.params.find((param) => param.id === id);
}
/**
* Sets all parameters for the X-API root.
* @param parameters - The XapiParameters object to set.
*/
setParameters(parameters) {
this.parameters = parameters;
}
/**
* Sets the value of a specific parameter, or adds it if it doesn't exist.
* @param id - The ID of the parameter.
* @param value - The value to set for the parameter.
*/
setParameter(id, value) {
const param = this.parameters.params.find((p) => p.id === id);
if (param) {
param.value = value;
} else {
this.addParameter({ id, value });
}
}
/**
* Returns the number of parameters.
* @returns The count of parameters.
*/
parameterSize() {
return this.parameters.params.length;
}
/**
* Returns the number of datasets.
* @returns The count of datasets.
*/
datasetSize() {
return this.datasets.length;
}
/**
* Iterates over the parameters.
* @returns An IterableIterator for parameters.
*/
*iterParameters() {
for (const param of this.parameters.params) {
yield param;
}
}
/**
* Iterates over the datasets.
* @returns An IterableIterator for datasets.
*/
*iterDatasets() {
for (const dataset of this.datasets) {
yield dataset;
}
}
};
var Dataset = class {
/** The ID of the dataset. */
id;
constColumns = [];
columns = [];
/** An array of rows in the dataset. */
rows = [];
_columnIndexMap = /* @__PURE__ */ new Map();
/**
* Creates an instance of Dataset.
* @param id - The ID of the dataset.
* @param constColumns - Initial array of constant columns.
* @param columns - Initial array of columns.
* @param rows - Initial array of rows.
*/
constructor(id, constColumns = [], columns = [], rows = []) {
this.id = id;
this.constColumns = constColumns;
this.columns = columns;
this.rows = rows;
}
/**
* Adds a column definition to the dataset.
* @param column - The column definition to add.
*/
addColumn(column) {
this.columns.push(column);
this._columnIndexMap.set(column.id, this.columns.length - 1);
}
/**
* Adds a constant column definition to the dataset.
* @param column - The constant column definition to add.
*/
addConstColumn(column) {
this.constColumns.push(column);
}
/**
* Creates a new empty row and adds it to the dataset.
* @returns The index of the newly created row.
*/
newRow() {
this.rows.push({ cols: [] });
return this.rows.length - 1;
}
/**
* Adds an existing row to the dataset.
* @param row - The row to add.
*/
addRow(row) {
this.rows.push(row);
}
/**
* Retrieves the index of a column by its ID.
* @param columnId - The ID of the column.
* @returns The index of the column, or undefined if not found.
*/
getColumnIndex(columnId) {
return this._columnIndexMap.get(columnId);
}
/**
* Retrieves the column definition by its ID.
* @param columnId - The ID of the column.
* @returns The Column object, or undefined if not found.
*/
getColumnInfo(columnId) {
const colIndex = this.getColumnIndex(columnId);
if (colIndex !== void 0) {
return this.columns[colIndex];
}
return void 0;
}
/**
* Retrieves the constant column definition by its ID.
* @param columnId - The ID of the constant column.
* @returns The ConstColumn object, or undefined if not found.
*/
getConstColumnInfo(columnId) {
return this.constColumns.find((col) => col.id === columnId);
}
/**
* Retrieves the value of a column in a specific row.
* @param rowIdx - The index of the row.
* @param columnId - The ID of the column.
* @returns The value of the column, or undefined if the row or column is not found.
* @throws {Error} if the column ID is not found in the dataset.
*/
getColumn(rowIdx, columnId) {
if (rowIdx < this.rows.length) {
const colIndex = this.getColumnIndex(columnId);
if (colIndex === void 0) {
throw new Error(`Column with id ${columnId} not found in dataset ${this.id}`);
}
const col = this.rows[rowIdx].cols[colIndex];
return col?.value;
}
return void 0;
}
/**
* Retrieves the original value of a column in a specific row (for OrgRow).
* @param rowIdx - The index of the row.
* @param columnId - The ID of the column.
* @returns The original value of the column, or undefined if the row or column is not found.
* @throws {Error} if the column ID is not found in the dataset.
*/
getOrgColumn(rowIdx, columnId) {
if (rowIdx < this.rows.length) {
const colIndex = this.getColumnIndex(columnId);
if (colIndex === void 0) {
throw new Error(`Column with id ${columnId} not found in dataset ${this.id}`);
}
const col = this.rows[rowIdx].orgRow?.[colIndex];
return col?.value;
}
return void 0;
}
/**
* Sets the value of a column in a specific row.
* @param rowIdx - The index of the row.
* @param columnId - The ID of the column.
* @param value - The value to set.
* @throws {Error} if the row index is out of bounds or the column ID is not found.
*/
setColumn(rowIdx, columnId, value) {
if (rowIdx < this.rows.length) {
const colIndex = this.getColumnIndex(columnId);
if (colIndex === void 0) {
throw new Error(`Column with id ${columnId} not found in dataset ${this.id}`);
}
this.rows[rowIdx].cols[colIndex] = { id: columnId, value };
} else {
throw new Error(`Row index ${rowIdx} out of bounds in dataset ${this.id}`);
}
}
/**
* Iterates over the constant column definitions.
* @returns An IterableIterator for constant columns.
*/
*iterConstColumns() {
for (const column of this.constColumns) {
yield column;
}
}
/**
* Iterates over the column definitions.
* @returns An IterableIterator for columns.
*/
*iterColumns() {
for (const column of this.columns) {
yield column;
}
}
/**
* Iterates over the rows in the dataset.
* @returns An IterableIterator for rows.
*/
*iterRows() {
for (const row of this.rows) {
yield row;
}
}
/**
* Returns the number of column definitions.
* @returns The count of columns.
*/
columnSize() {
return this.columns.length;
}
/**
* Returns the number of constant column definitions.
* @returns The count of constant columns.
*/
constColumnSize() {
return this.constColumns.length;
}
/**
* Returns the number of rows in the dataset.
* @returns The count of rows.
*/
rowSize() {
return this.rows.length;
}
};
// src/handler.ts
var defaultOptions = {
xapiVersion: NexaVersion,
parseToTypes: true
};
var _options = {
...defaultOptions
};
function initXapi(options) {
_options = {
...options
};
}
function parse2(xml) {
const parsedXml = txml.parse(xml);
const xapiRoot = new XapiRoot();
const rootElement = parsedXml.find((node) => {
const rootNode = node;
return rootNode.tagName === "Root";
});
if (rootElement === void 0) return xapiRoot;
const parametersElement = rootElement.children?.find((node) => node.tagName === "Parameters");
parseParameters(parametersElement, xapiRoot);
const datasetsElements = rootElement.children?.filter((node) => node.tagName === "Dataset");
if (datasetsElements && datasetsElements?.length && datasetsElements.length > 0) {
for (const datasetsElement of datasetsElements) {
if (!datasetsElement.attributes || !datasetsElement.attributes.id) {
throw new InvalidXmlError("Dataset element must have an 'id' attribute");
}
const datasetId = datasetsElement.attributes?.id;
const dataset = new Dataset(datasetId);
xapiRoot.addDataset(dataset);
const columnInfoElement = datasetsElement.children?.find((node) => node.tagName === "ColumnInfo");
parseColumnInfo(columnInfoElement, dataset);
const rowsElement = datasetsElement.children?.find((node) => node.tagName === "Rows");
parseRows(rowsElement, dataset);
}
}
return xapiRoot;
}
function parseValue(value, type = "STRING") {
value = _unescapeXml(value);
return _options.parseToTypes ? convertToColumnType(value, type) : value;
}
function parseColumnInfo(columnInfoElement, dataset) {
if (!columnInfoElement) throw new InvalidXmlError("ColumnInfo element is missing in the dataset");
columnInfoElement.children?.forEach((colInfo) => {
if (colInfo.tagName === "ConstColumn") {
dataset.addConstColumn({
id: colInfo.attributes?.id,
size: parseInt(colInfo.attributes?.size || "0", 10),
type: colInfo.attributes?.type || "STRING",
value: parseValue(colInfo.attributes?.value, colInfo.attributes?.type)
});
} else if (colInfo.tagName === "Column") {
dataset.addColumn({
id: colInfo.attributes?.id,
size: parseInt(colInfo.attributes?.size || "0", 10),
type: colInfo.attributes?.type || "STRING"
});
}
});
}
function parseRows(rowsElement, dataset) {
rowsElement?.children?.forEach((r) => {
if (r.tagName === "Row") {
const rowIndex = dataset.newRow();
dataset.rows[rowIndex].type = r.attributes?.type || void 0;
r.children?.forEach((col) => {
if (col.tagName === "Col") {
const colId = col.attributes?.id;
const value = col.children?.[0];
const columnInfo = dataset.getColumnInfo(colId);
if (!columnInfo) throw new InvalidXmlError(`Column with id ${colId} not found in dataset ${dataset.id}`);
const castedValue = parseValue(value, columnInfo.type);
dataset.rows[rowIndex].cols.push({ id: colId, value: castedValue });
} else if (col.tagName === "OrgRow") {
dataset.rows[rowIndex].orgRow = [];
col.children?.forEach((orgCol) => {
if (orgCol.tagName === "Col") {
const colId = orgCol.attributes?.id;
const value = orgCol.children?.[0];
const columnInfo = dataset.getColumnInfo(colId);
const castedValue = parseValue(value, columnInfo.type);
if (dataset && dataset.rows && dataset.rows[rowIndex] && dataset.rows[rowIndex].orgRow) {
dataset.rows[rowIndex].orgRow.push({ id: colId, value: castedValue });
}
}
});
}
});
} else if (r.tagName === "Col") {
throw new InvalidXmlError("Row must be defined before Col");
} else if (r.tagName === "OrgRow") {
throw new InvalidXmlError("Row must be defined before OrgRow");
}
});
}
function parseParameters(parametersElement, xapiRoot) {
if (!parametersElement) return;
parametersElement.children?.forEach((p) => {
if (p.tagName === "Parameter") {
const id = p.attributes?.id;
const type = p.attributes?.type || "STRING";
const value = p.children?.[0];
xapiRoot.addParameter({
id,
type,
value: parseValue(value, type)
});
}
});
}
async function writeString(root) {
const stringStream = new StringWritableStream();
await write(stringStream, root);
return stringStream.getResult();
}
async function write(stream, root) {
const writer = new import_stax_xml.StaxXmlWriter(stream, {
addEntities: makeWriterEntities(),
encoding: "UTF-8",
indentString: " ",
prettyPrint: true
});
await writer.writeStartDocument("1.0", "UTF-8");
await writer.writeStartElement("Root", {
// Root
attributes: {
xmlns: _options.xapiVersion?.xmlns || NexaVersion.xmlns,
version: _options.xapiVersion?.version || NexaVersion.version
}
});
if (root.parameterSize() > 0) {
await writeParameters(writer, root.iterParameters());
}
if (root.datasetSize() > 0) {
await writer.writeStartElement("Datasets");
for (const dataset of root.iterDatasets()) {
await writeDataset(writer, dataset);
}
await writer.writeEndElement();
}
await writer.writeEndElement();
}
async function writeParameters(writer, iterator) {
if (iterator) {
await writer.writeStartElement("Parameters");
for (const parameter of iterator) {
await writer.writeStartElement("Parameter", { attributes: { id: parameter.id } });
if (parameter.type !== void 0) {
await writer.writeAttribute("type", parameter.type);
}
if (parameter.value !== void 0) {
if (typeof parameter.value === "string") {
await writer.writeAttribute("value", parameter.value);
} else if (parameter.value instanceof Date) {
await writer.writeAttribute("value", dateToString(parameter.value, parameter.type));
} else if (parameter.value instanceof Uint8Array) {
await writer.writeAttribute("value", uint8ArrayToBase64(parameter.value));
} else {
await writer.writeAttribute("value", String(parameter.value));
}
}
await writer.writeEndElement();
}
await writer.writeEndElement();
}
}
async function writeDataset(writer, dataset) {
await writer.writeStartElement("Dataset", { attributes: { id: dataset.id } });
if (dataset.constColumnSize() > 0 || dataset.columnSize() > 0) {
await writer.writeStartElement("ColumnInfo");
for (const constCol of dataset.iterConstColumns()) {
await writer.writeStartElement("ConstColumn", {
attributes: {
id: constCol.id,
size: String(constCol.size),
type: constCol.type,
value: constCol.value !== void 0 ? String(constCol.value) : ""
},
selfClosing: true
});
}
for (const col of dataset.iterColumns()) {
await writer.writeStartElement("Column", {
attributes: {
id: col.id,
size: String(col.size),
type: col.type
},
selfClosing: true
});
}
await writer.writeEndElement();
}
await writer.writeStartElement("Rows");
for (const row of dataset.iterRows()) {
if (row.type) {
await writer.writeStartElement("Row", { attributes: { type: row.type } });
} else {
await writer.writeStartElement("Row");
}
for (const col of row.cols) {
await writeColumn(writer, dataset, col);
}
if (row.orgRow && row.orgRow.length > 0) {
await writer.writeStartElement("OrgRow");
for (const orgCol of row.orgRow) {
await writeColumn(writer, dataset, orgCol);
}
await writer.writeEndElement();
}
await writer.writeEndElement();
}
await writer.writeEndElement();
await writer.writeEndElement();
}
async function writeColumn(writer, dataset, col) {
if (col.value !== void 0 && col.value !== null) {
const colInfo = dataset.getColumnInfo(col.id);
await writer.writeStartElement("Col", { attributes: { id: col.id } });
await writer.writeCharacters(convertToString(col.value, colInfo.type));
await writer.writeEndElement();
} else {
await writer.writeStartElement("Col", { attributes: { id: col.id }, selfClosing: true });
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ColumnTypeError,
Dataset,
InvalidXmlError,
NexaVersion,
StringWritableStream,
XapiRoot,
XplatformVersion,
_unescapeXml,
arrayBufferToString,
base64ToUint8Array,
columnType,
convertToColumnType,
convertToString,
dateToString,
initXapi,
makeParseEntities,
makeWriterEntities,
parse,
rowType,
stringToDate,
stringToReadableStream,
uint8ArrayToBase64,
write,
writeString
});