UNPKG

@xapi-js/core

Version:

Core utilities and types for X-API.

806 lines (799 loc) 25.3 kB
"use strict"; 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 });