@xapi-js/core 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,31 +1,4 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
- let stax_xml = require("stax-xml");
25
- stax_xml = __toESM(stax_xml);
26
- let txml = require("txml");
27
- txml = __toESM(txml);
28
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
29
2
  //#region src/types.ts
30
3
  /**
31
4
  * Defines the possible types of rows in an X-API dataset (e.g., "insert", "update", "delete").
@@ -78,7 +51,6 @@ var InvalidXmlError = class extends Error {
78
51
  this.name = "InvalidXmlError";
79
52
  }
80
53
  };
81
-
82
54
  //#endregion
83
55
  //#region src/xapi-data.ts
84
56
  /**
@@ -163,18 +135,18 @@ var XapiRoot = class {
163
135
  return this.datasets.length;
164
136
  }
165
137
  /**
166
- * Iterates over the parameters.
167
- * @returns An IterableIterator for parameters.
138
+ * Returns all parameters as an array.
139
+ * @returns An array of parameters.
168
140
  */
169
- *iterParameters() {
170
- for (const param of this.parameters.params) yield param;
141
+ getParameters() {
142
+ return this.parameters.params;
171
143
  }
172
144
  /**
173
- * Iterates over the datasets.
174
- * @returns An IterableIterator for datasets.
145
+ * Returns all datasets as an array.
146
+ * @returns An array of datasets.
175
147
  */
176
- *iterDatasets() {
177
- for (const dataset of this.datasets) yield dataset;
148
+ getDatasets() {
149
+ return this.datasets;
178
150
  }
179
151
  };
180
152
  /**
@@ -302,25 +274,25 @@ var Dataset = class {
302
274
  } else throw new Error(`Row index ${rowIdx} out of bounds in dataset ${this.id}`);
303
275
  }
304
276
  /**
305
- * Iterates over the constant column definitions.
306
- * @returns An IterableIterator for constant columns.
277
+ * Returns all constant column definitions as an array.
278
+ * @returns An array of constant columns.
307
279
  */
308
- *iterConstColumns() {
309
- for (const column of this.constColumns) yield column;
280
+ getConstColumns() {
281
+ return this.constColumns;
310
282
  }
311
283
  /**
312
- * Iterates over the column definitions.
313
- * @returns An IterableIterator for columns.
284
+ * Returns all column definitions as an array.
285
+ * @returns An array of columns.
314
286
  */
315
- *iterColumns() {
316
- for (const column of this.columns) yield column;
287
+ getColumns() {
288
+ return this.columns;
317
289
  }
318
290
  /**
319
- * Iterates over the rows in the dataset.
320
- * @returns An IterableIterator for rows.
291
+ * Returns all rows in the dataset as an array.
292
+ * @returns An array of rows.
321
293
  */
322
- *iterRows() {
323
- for (const row of this.rows) yield row;
294
+ getRows() {
295
+ return this.rows;
324
296
  }
325
297
  /**
326
298
  * Returns the number of column definitions.
@@ -344,35 +316,30 @@ var Dataset = class {
344
316
  return this.rows.length;
345
317
  }
346
318
  };
347
-
348
319
  //#endregion
349
320
  //#region src/utils.ts
350
321
  function arrayBufferToString(buffer) {
351
322
  return new TextDecoder().decode(buffer);
352
323
  }
353
324
  /**
354
- * Creates an array of entities for parsing XML, including control characters.
355
- * @returns An array of objects, each with an `entity` (e.g., "&#1;") and its `value` (e.g., "\x01").
325
+ * Pre-defined XML entities for parsing, including control characters.
326
+ * Static array to avoid repeated allocation.
356
327
  */
357
- function makeParseEntities() {
358
- const entities$1 = [];
359
- for (let i = 1; i <= 32; i++) entities$1.push({
328
+ const PARSE_ENTITIES = (() => {
329
+ const entities = [];
330
+ for (let i = 1; i <= 32; i++) entities.push({
360
331
  entity: `&#${i};`,
361
332
  value: String.fromCharCode(i)
362
333
  });
363
- return entities$1;
364
- }
334
+ return entities;
335
+ })();
365
336
  /**
366
- * Creates an array of entities for writing XML, including control characters.
337
+ * Creates an array of entities for parsing XML, including control characters.
338
+ * @deprecated Use PARSE_ENTITIES directly for better performance
367
339
  * @returns An array of objects, each with an `entity` (e.g., "&#1;") and its `value` (e.g., "\x01").
368
340
  */
369
- function makeWriterEntities() {
370
- const entities$1 = [];
371
- for (let i = 1; i <= 32; i++) entities$1.push({
372
- entity: `&#${i};`,
373
- value: String.fromCharCode(i)
374
- });
375
- return entities$1;
341
+ function makeParseEntities() {
342
+ return PARSE_ENTITIES.slice();
376
343
  }
377
344
  /**
378
345
  * Converts a Base64 string to a Uint8Array.
@@ -501,10 +468,10 @@ function stringToReadableStream(str) {
501
468
  function convertToColumnType(value, type) {
502
469
  switch (type) {
503
470
  case "INT":
504
- case "BIGDECIMAL":
505
471
  const intValue = parseInt(value, 10);
506
472
  return isNaN(intValue) ? value : intValue;
507
473
  case "FLOAT":
474
+ case "BIGDECIMAL":
508
475
  const floatValue = parseFloat(value);
509
476
  return isNaN(floatValue) ? value : floatValue;
510
477
  case "DECIMAL":
@@ -542,19 +509,338 @@ function convertToString(value, type) {
542
509
  default: return String(value);
543
510
  }
544
511
  }
545
- const entities = makeParseEntities();
512
+ /**
513
+ * Pre-compiled regex for control character entities.
514
+ * Created once to avoid repeated compilation.
515
+ */
516
+ const CONTROL_CHAR_ENTITY_REGEX = new RegExp(PARSE_ENTITIES.map((e) => e.entity.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
517
+ /**
518
+ * Map for fast entity lookup during decoding.
519
+ */
520
+ const ENTITY_MAP = new Map(PARSE_ENTITIES.map((e) => [e.entity, e.value]));
546
521
  function _unescapeXml(str) {
547
522
  if (!str) return str;
548
- const regex = new RegExp(entities.map((e) => e.entity).join("|"), "g");
549
- return str.replace(regex, (match) => {
550
- const entity = entities.find((e) => e.entity === match);
551
- return entity ? entity.value : match;
523
+ if (str.indexOf("&") === -1) return str;
524
+ let result = str.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&apos;/g, "'").replace(/&amp;/g, "&");
525
+ result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => {
526
+ return ENTITY_MAP.get(match) || match;
552
527
  });
528
+ return result;
553
529
  }
554
530
  function isXapiRoot(value) {
555
531
  return value instanceof XapiRoot;
556
532
  }
557
-
533
+ /**
534
+ * Escapes XML special characters in a string.
535
+ * @param str - The string to escape.
536
+ * @returns The escaped string.
537
+ */
538
+ function escapeXml(str) {
539
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
540
+ }
541
+ /**
542
+ * Encodes control characters as XML entities, excluding standard whitespace.
543
+ * Encodes 0x01-0x08, 0x0B-0x0C, 0x0E-0x1F but NOT 0x09 (tab), 0x0A (LF), 0x0D (CR), 0x20 (space).
544
+ * @param str - The string to encode.
545
+ * @returns The encoded string.
546
+ */
547
+ function encodeControlChars(str) {
548
+ let result = "";
549
+ for (let i = 0; i < str.length; i++) {
550
+ const charCode = str.charCodeAt(i);
551
+ if (charCode >= 1 && charCode <= 8 || charCode >= 11 && charCode <= 12 || charCode >= 14 && charCode <= 31) result += `&#${charCode};`;
552
+ else result += str[i];
553
+ }
554
+ return result;
555
+ }
556
+ /**
557
+ * A string builder for generating formatted XML.
558
+ */
559
+ var XmlStringBuilder = class {
560
+ lines = [];
561
+ indentLevel = 0;
562
+ indentString = " ";
563
+ /**
564
+ * Writes the XML declaration.
565
+ * @param version - The XML version (default: "1.0").
566
+ * @param encoding - The encoding (default: "UTF-8").
567
+ */
568
+ writeDeclaration(version = "1.0", encoding = "UTF-8") {
569
+ this.lines.push(`<?xml version="${version}" encoding="${encoding}"?>`);
570
+ }
571
+ /**
572
+ * Writes a start element tag.
573
+ * @param name - The element name.
574
+ * @param attributes - Optional attributes object.
575
+ * @param selfClosing - Whether to create a self-closing tag.
576
+ */
577
+ writeStartElement(name, attributes, selfClosing) {
578
+ let tag = `${this.indentString.repeat(this.indentLevel)}<${name}`;
579
+ if (attributes) for (const [key, value] of Object.entries(attributes)) {
580
+ const encodedValue = encodeControlChars(escapeXml(value));
581
+ tag += ` ${key}="${encodedValue}"`;
582
+ }
583
+ if (selfClosing) {
584
+ tag += "/>";
585
+ this.lines.push(tag);
586
+ } else {
587
+ tag += ">";
588
+ this.lines.push(tag);
589
+ this.indentLevel++;
590
+ }
591
+ }
592
+ /**
593
+ * Writes an end element tag.
594
+ * @param name - The element name.
595
+ */
596
+ writeEndElement(name) {
597
+ this.indentLevel--;
598
+ const indent = this.indentString.repeat(this.indentLevel);
599
+ this.lines.push(`${indent}</${name}>`);
600
+ }
601
+ /**
602
+ * Writes an element with text content.
603
+ * @param name - The element name.
604
+ * @param attributes - Optional attributes object.
605
+ * @param text - The text content.
606
+ */
607
+ writeElementWithText(name, attributes, text) {
608
+ let tag = `${this.indentString.repeat(this.indentLevel)}<${name}`;
609
+ if (attributes) for (const [key, value] of Object.entries(attributes)) {
610
+ const encodedValue = encodeControlChars(escapeXml(value));
611
+ tag += ` ${key}="${encodedValue}"`;
612
+ }
613
+ const encodedText = encodeControlChars(escapeXml(text));
614
+ tag += `>${encodedText}</${name}>`;
615
+ this.lines.push(tag);
616
+ }
617
+ /**
618
+ * Returns the generated XML string.
619
+ * @returns The complete XML string with line breaks.
620
+ */
621
+ toString() {
622
+ return this.lines.join("\n") + "\n";
623
+ }
624
+ };
625
+ /**
626
+ * Parses XML string into an XML node structure.
627
+ * This is a lightweight parser optimized for X-API XML structure.
628
+ *
629
+ * @param xml - The XML string to parse.
630
+ * @returns An array of parsed XML nodes.
631
+ */
632
+ function parseXml(xml) {
633
+ if (!xml || xml.trim() === "") return [];
634
+ return new XapiXmlParser(xml).parse();
635
+ }
636
+ /**
637
+ * Character codes for fast comparison
638
+ */
639
+ const CHAR_SPACE = 32;
640
+ const CHAR_TAB = 9;
641
+ const CHAR_LF = 10;
642
+ const CHAR_CR = 13;
643
+ const CHAR_GT = 62;
644
+ const CHAR_SLASH = 47;
645
+ const CHAR_EQUALS = 61;
646
+ const CHAR_QUOTE = 34;
647
+ const CHAR_APOS = 39;
648
+ /**
649
+ * A lightweight XML parser optimized for X-API structure.
650
+ */
651
+ var XapiXmlParser = class {
652
+ xml;
653
+ pos = 0;
654
+ length;
655
+ constructor(xml) {
656
+ this.xml = xml;
657
+ this.length = xml.length;
658
+ }
659
+ parse() {
660
+ const nodes = [];
661
+ while (this.pos < this.length) {
662
+ this.skipWhitespace();
663
+ if (this.pos >= this.length) break;
664
+ if (this.matches("<?xml")) {
665
+ this.skipUntil("?>");
666
+ this.pos += 2;
667
+ continue;
668
+ }
669
+ if (this.matches("<!--")) {
670
+ this.skipUntil("-->");
671
+ this.pos += 3;
672
+ continue;
673
+ }
674
+ if (this.current() === "<") {
675
+ const node = this.parseElement();
676
+ if (node) nodes.push(node);
677
+ } else this.pos++;
678
+ }
679
+ return nodes;
680
+ }
681
+ parseElement() {
682
+ if (this.current() !== "<") return null;
683
+ this.pos++;
684
+ if (this.current() === "/") return null;
685
+ const tagName = this.parseTagName();
686
+ if (!tagName) return null;
687
+ const node = {
688
+ tagName,
689
+ attributes: void 0,
690
+ children: void 0
691
+ };
692
+ const attributes = this.parseAttributes();
693
+ if (attributes && Object.keys(attributes).length > 0) node.attributes = attributes;
694
+ this.skipWhitespace();
695
+ if (this.matches("/>")) {
696
+ this.pos += 2;
697
+ return node;
698
+ }
699
+ if (this.current() === ">") this.pos++;
700
+ const children = [];
701
+ let textContent = "";
702
+ while (this.pos < this.length) {
703
+ if (this.matches("<![CDATA[")) {
704
+ const cdataText = this.parseCDATA();
705
+ if (cdataText !== null) textContent += cdataText;
706
+ continue;
707
+ }
708
+ if (this.matches("</")) {
709
+ if (textContent) children.push(textContent);
710
+ this.pos += 2;
711
+ this.skipUntil(">");
712
+ this.pos++;
713
+ break;
714
+ }
715
+ if (this.current() === "<") {
716
+ if (textContent.trim()) {
717
+ children.push(textContent);
718
+ textContent = "";
719
+ }
720
+ const child = this.parseElement();
721
+ if (child) children.push(child);
722
+ } else {
723
+ textContent += this.current();
724
+ this.pos++;
725
+ }
726
+ }
727
+ if (children.length > 0) node.children = children;
728
+ return node;
729
+ }
730
+ parseTagName() {
731
+ const start = this.pos;
732
+ while (this.pos < this.length) {
733
+ const code = this.currentCharCode();
734
+ if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
735
+ this.pos++;
736
+ }
737
+ return this.xml.substring(start, this.pos);
738
+ }
739
+ parseAttributes() {
740
+ const attrs = {};
741
+ while (this.pos < this.length) {
742
+ this.skipWhitespace();
743
+ const ch = this.current();
744
+ if (ch === ">" || ch === "/" || this.matches("/>")) break;
745
+ const attrName = this.parseAttributeName();
746
+ if (!attrName) break;
747
+ this.skipWhitespace();
748
+ if (this.current() === "=") this.pos++;
749
+ this.skipWhitespace();
750
+ attrs[attrName] = this.parseAttributeValue();
751
+ }
752
+ return Object.keys(attrs).length > 0 ? attrs : void 0;
753
+ }
754
+ parseAttributeName() {
755
+ const start = this.pos;
756
+ while (this.pos < this.length) {
757
+ const code = this.currentCharCode();
758
+ if (code === CHAR_EQUALS || this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
759
+ this.pos++;
760
+ }
761
+ return this.xml.substring(start, this.pos);
762
+ }
763
+ parseAttributeValue() {
764
+ const quoteCode = this.currentCharCode();
765
+ if (quoteCode !== CHAR_QUOTE && quoteCode !== CHAR_APOS) {
766
+ const start = this.pos;
767
+ while (this.pos < this.length) {
768
+ const code = this.currentCharCode();
769
+ if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
770
+ this.pos++;
771
+ }
772
+ return this.xml.substring(start, this.pos);
773
+ }
774
+ this.pos++;
775
+ const start = this.pos;
776
+ while (this.pos < this.length) {
777
+ if (this.currentCharCode() === quoteCode) {
778
+ const value = this.xml.substring(start, this.pos);
779
+ this.pos++;
780
+ return value;
781
+ }
782
+ this.pos++;
783
+ }
784
+ return this.xml.substring(start, this.pos);
785
+ }
786
+ parseCDATA() {
787
+ if (!this.matches("<![CDATA[")) return null;
788
+ this.pos += 9;
789
+ const endPos = this.findString("]]>");
790
+ if (endPos === -1) {
791
+ const content = this.xml.substring(this.pos);
792
+ this.pos = this.length;
793
+ return content;
794
+ }
795
+ const content = this.xml.substring(this.pos, endPos);
796
+ this.pos = endPos + 3;
797
+ return content;
798
+ }
799
+ skipWhitespace() {
800
+ while (this.pos < this.length) {
801
+ const code = this.currentCharCode();
802
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
803
+ this.pos++;
804
+ }
805
+ }
806
+ isWhitespace(code) {
807
+ return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR;
808
+ }
809
+ skipUntil(target) {
810
+ const pos = this.findString(target);
811
+ if (pos !== -1) this.pos = pos;
812
+ else this.pos = this.length;
813
+ }
814
+ current() {
815
+ return this.xml[this.pos];
816
+ }
817
+ currentCharCode() {
818
+ return this.xml.charCodeAt(this.pos);
819
+ }
820
+ /**
821
+ * Checks if the string at the given position matches the target string.
822
+ * This avoids creating substring instances for comparison.
823
+ * @param str - The target string to match
824
+ * @param pos - The position to check from (default: current position)
825
+ */
826
+ matches(str, pos = this.pos) {
827
+ const len = str.length;
828
+ if (pos + len > this.length) return false;
829
+ for (let i = 0; i < len; i++) if (this.xml.charCodeAt(pos + i) !== str.charCodeAt(i)) return false;
830
+ return true;
831
+ }
832
+ /**
833
+ * Finds the position of a target string starting from the given position.
834
+ * @param str - The string to find
835
+ * @param startPos - Starting position (default: current position)
836
+ * @returns The position where the string is found, or -1 if not found
837
+ */
838
+ findString(str, startPos = this.pos) {
839
+ const maxPos = this.length - str.length;
840
+ for (let pos = startPos; pos <= maxPos; pos++) if (this.matches(str, pos)) return pos;
841
+ return -1;
842
+ }
843
+ };
558
844
  //#endregion
559
845
  //#region src/handler.ts
560
846
  let _options = {
@@ -570,12 +856,12 @@ function initXapi(options) {
570
856
  _options = { ...options };
571
857
  }
572
858
  /**
573
- * Parses an XML string into an XapiRoot object using txml.
859
+ * Parses an XML string into an XapiRoot object using custom parser.
574
860
  * @param xml - The string containing the XML data.
575
- * @returns A Promise that resolves to an XapiRoot object.
861
+ * @returns An XapiRoot object.
576
862
  */
577
863
  function parse(xml) {
578
- const parsedXml = txml.parse(xml);
864
+ const parsedXml = parseXml(xml);
579
865
  const xapiRoot = new XapiRoot();
580
866
  let rootElement;
581
867
  for (let i = 0; i < parsedXml.length; i++) if (parsedXml[i].tagName === "Root") {
@@ -586,121 +872,130 @@ function parse(xml) {
586
872
  const rootChildren = rootElement.children;
587
873
  if (rootChildren && rootChildren.length > 0) for (let i = 0; i < rootChildren.length; i++) {
588
874
  const child = rootChildren[i];
875
+ if (typeof child === "string") continue;
589
876
  const tagName = child.tagName;
590
877
  if (tagName === "Parameters") parseParameters(child, xapiRoot);
591
- else if (tagName === "Dataset") {
592
- if (!child.attributes || !child.attributes.id) throw new InvalidXmlError("Dataset element must have an 'id' attribute");
593
- const datasetId = child.attributes.id;
594
- const dataset = new Dataset(datasetId);
595
- xapiRoot.addDataset(dataset);
596
- const datasetChildren = child.children;
597
- let columnInfoElement;
598
- let rowsElement;
599
- if (datasetChildren) for (let j = 0; j < datasetChildren.length; j++) {
600
- const datasetChild = datasetChildren[j];
601
- const childTagName = datasetChild.tagName;
602
- if (childTagName === "ColumnInfo") columnInfoElement = datasetChild;
603
- else if (childTagName === "Rows") rowsElement = datasetChild;
604
- if (columnInfoElement && rowsElement) break;
605
- }
606
- parseColumnInfo(columnInfoElement, dataset);
607
- parseRows(rowsElement, dataset);
608
- }
878
+ else if (tagName === "Datasets") {
879
+ for (const datasetElement of child.children ?? []) if (typeof datasetElement !== "string" && datasetElement.tagName === "Dataset") parseDataset(datasetElement, xapiRoot);
880
+ } else if (tagName === "Dataset") parseDataset(child, xapiRoot);
609
881
  }
610
882
  return xapiRoot;
611
883
  }
884
+ function parseDataset(child, xapiRoot) {
885
+ if (!child.attributes || !child.attributes.id) throw new InvalidXmlError("Dataset element must have an 'id' attribute");
886
+ const datasetId = child.attributes.id;
887
+ const dataset = new Dataset(datasetId);
888
+ xapiRoot.addDataset(dataset);
889
+ const datasetChildren = child.children;
890
+ let columnInfoElement;
891
+ let rowsElement;
892
+ if (datasetChildren) for (let j = 0; j < datasetChildren.length; j++) {
893
+ const datasetChild = datasetChildren[j];
894
+ if (typeof datasetChild === "string") continue;
895
+ const childTagName = datasetChild.tagName;
896
+ if (childTagName === "ColumnInfo") columnInfoElement = datasetChild;
897
+ else if (childTagName === "Rows") rowsElement = datasetChild;
898
+ if (columnInfoElement && rowsElement) break;
899
+ }
900
+ parseColumnInfo(columnInfoElement, dataset);
901
+ parseRows(rowsElement, dataset);
902
+ }
612
903
  function parseValue(value, type = "STRING") {
613
904
  value = _unescapeXml(value);
614
905
  return _options.parseToTypes ? convertToColumnType(value, type) : value;
615
906
  }
616
907
  function parseColumnInfo(columnInfoElement, dataset) {
617
- if (!columnInfoElement) throw new InvalidXmlError("ColumnInfo element is missing in the dataset");
908
+ if (!columnInfoElement || typeof columnInfoElement === "string") throw new InvalidXmlError("ColumnInfo element is missing in the dataset");
618
909
  const children = columnInfoElement.children;
619
910
  if (!children) return;
620
911
  for (let i = 0; i < children.length; i++) {
621
912
  const colInfo = children[i];
622
- const tagName = colInfo.tagName;
623
- if (tagName === "ConstColumn") {
624
- const attrs = colInfo.attributes;
625
- const sizeStr = attrs?.size;
626
- dataset.addConstColumn({
627
- id: attrs?.id,
628
- size: sizeStr ? parseInt(sizeStr, 10) : 0,
629
- type: attrs?.type || "STRING",
630
- value: parseValue(attrs?.value, attrs?.type)
631
- });
632
- } else if (tagName === "Column") {
633
- const attrs = colInfo.attributes;
634
- const sizeStr = attrs?.size;
635
- dataset.addColumn({
636
- id: attrs?.id,
637
- size: sizeStr ? parseInt(sizeStr, 10) : 0,
638
- type: attrs?.type || "STRING"
639
- });
913
+ if (typeof colInfo !== "string") {
914
+ const tagName = colInfo.tagName;
915
+ if (tagName === "ConstColumn") {
916
+ const attrs = colInfo.attributes;
917
+ const sizeStr = attrs?.size;
918
+ dataset.addConstColumn({
919
+ id: attrs?.id,
920
+ size: sizeStr ? parseInt(sizeStr, 10) : 0,
921
+ type: attrs?.type || "STRING",
922
+ value: parseValue(attrs?.value, attrs?.type)
923
+ });
924
+ } else if (tagName === "Column") {
925
+ const attrs = colInfo.attributes;
926
+ const sizeStr = attrs?.size;
927
+ dataset.addColumn({
928
+ id: attrs?.id,
929
+ size: sizeStr ? parseInt(sizeStr, 10) : 0,
930
+ type: attrs?.type || "STRING"
931
+ });
932
+ }
640
933
  }
641
934
  }
642
935
  }
643
936
  function parseRows(rowsElement, dataset) {
937
+ if (typeof rowsElement === "string") return;
644
938
  const rows = rowsElement?.children;
645
939
  if (!rows) return;
646
940
  const datasetRows = dataset.rows;
647
941
  for (let i = 0; i < rows.length; i++) {
648
942
  const r = rows[i];
649
- const tagName = r.tagName;
650
- if (tagName === "Row") {
651
- const rowIndex = dataset.newRow();
652
- const currentRow = datasetRows[rowIndex];
653
- currentRow.type = r.attributes?.type || void 0;
654
- const cols = r.children;
655
- if (!cols) continue;
656
- for (let j = 0; j < cols.length; j++) {
657
- const col = cols[j];
658
- const colTagName = col.tagName;
659
- if (colTagName === "Col") {
660
- const colId = col.attributes?.id;
661
- const value = col.children?.[0];
662
- const columnInfo = dataset.getColumnInfo(colId);
663
- if (!columnInfo) throw new InvalidXmlError(`Column with id ${colId} not found in dataset ${dataset.id}`);
664
- const castedValue = parseValue(value, columnInfo.type);
665
- currentRow.cols.push({
666
- id: colId,
667
- value: castedValue
668
- });
669
- } else if (colTagName === "OrgRow") {
670
- const orgRow = [];
671
- currentRow.orgRow = orgRow;
672
- const orgCols = col.children;
673
- if (!orgCols) continue;
674
- for (let k = 0; k < orgCols.length; k++) {
675
- const orgCol = orgCols[k];
676
- if (orgCol.tagName === "Col") {
677
- const colId = orgCol.attributes?.id;
678
- const value = orgCol.children?.[0];
943
+ if (typeof r !== "string") {
944
+ const tagName = r.tagName;
945
+ if (tagName === "Row") {
946
+ const currentRow = datasetRows[dataset.newRow()];
947
+ currentRow.type = r.attributes?.type || void 0;
948
+ const cols = r.children;
949
+ if (cols) for (let j = 0; j < cols.length; j++) {
950
+ const col = cols[j];
951
+ if (typeof col !== "string") {
952
+ const colTagName = col.tagName;
953
+ if (colTagName === "Col") {
954
+ const colId = col.attributes?.id;
955
+ const value = col.children?.[0];
679
956
  const columnInfo = dataset.getColumnInfo(colId);
957
+ if (!columnInfo) throw new InvalidXmlError(`Column with id ${colId} not found in dataset ${dataset.id}`);
680
958
  const castedValue = parseValue(value, columnInfo.type);
681
- orgRow.push({
959
+ currentRow.cols.push({
682
960
  id: colId,
683
961
  value: castedValue
684
962
  });
963
+ } else if (colTagName === "OrgRow") {
964
+ const orgRow = [];
965
+ currentRow.orgRow = orgRow;
966
+ const orgCols = col.children;
967
+ if (orgCols) for (let k = 0; k < orgCols.length; k++) {
968
+ const orgCol = orgCols[k];
969
+ if (typeof orgCol !== "string" && orgCol.tagName === "Col") {
970
+ const colId = orgCol.attributes?.id;
971
+ const value = orgCol.children?.[0];
972
+ const castedValue = parseValue(value, dataset.getColumnInfo(colId).type);
973
+ orgRow.push({
974
+ id: colId,
975
+ value: castedValue
976
+ });
977
+ }
978
+ }
685
979
  }
686
980
  }
687
981
  }
688
- }
689
- } else if (tagName === "Col") throw new InvalidXmlError("Row must be defined before Col");
690
- else if (tagName === "OrgRow") throw new InvalidXmlError("Row must be defined before OrgRow");
982
+ } else if (tagName === "Col") throw new InvalidXmlError("Row must be defined before Col");
983
+ else if (tagName === "OrgRow") throw new InvalidXmlError("Row must be defined before OrgRow");
984
+ }
691
985
  }
692
986
  }
693
987
  function parseParameters(parametersElement, xapiRoot) {
988
+ if (typeof parametersElement === "string") return;
694
989
  if (!parametersElement) return;
695
990
  const children = parametersElement.children;
696
991
  if (!children) return;
697
992
  for (let i = 0; i < children.length; i++) {
698
993
  const p = children[i];
699
- if (p.tagName === "Parameter") {
994
+ if (typeof p !== "string" && p.tagName === "Parameter") {
700
995
  const attrs = p.attributes;
701
996
  const id = attrs?.id;
702
997
  const type = attrs?.type || "STRING";
703
- const value = p.children?.[0];
998
+ const value = p.children?.[0] ?? attrs?.value;
704
999
  xapiRoot.addParameter({
705
1000
  id,
706
1001
  type,
@@ -710,91 +1005,179 @@ function parseParameters(parametersElement, xapiRoot) {
710
1005
  }
711
1006
  }
712
1007
  function write(root) {
713
- const writer = new stax_xml.StaxXmlWriterSync({
714
- addEntities: makeWriterEntities(),
715
- encoding: "UTF-8",
716
- indentString: " ",
717
- prettyPrint: true
718
- });
719
- writer.writeStartDocument("1.0", "UTF-8");
720
- writer.writeStartElement("Root", { attributes: {
1008
+ const builder = new XmlStringBuilder();
1009
+ builder.writeDeclaration("1.0", "UTF-8");
1010
+ builder.writeStartElement("Root", {
721
1011
  xmlns: _options.xapiVersion?.xmlns || NexaVersion.xmlns,
722
1012
  version: _options.xapiVersion?.version || NexaVersion.version
723
- } });
724
- if (root.parameterSize() > 0) writeParameters(writer, root.iterParameters());
1013
+ });
1014
+ if (root.parameterSize() > 0) writeParameters(builder, root.getParameters());
725
1015
  if (root.datasetSize() > 0) {
726
- writer.writeStartElement("Datasets");
727
- for (const dataset of root.iterDatasets()) writeDataset(writer, dataset);
728
- writer.writeEndElement();
1016
+ builder.writeStartElement("Datasets");
1017
+ for (const dataset of root.getDatasets()) writeDataset(builder, dataset);
1018
+ builder.writeEndElement("Datasets");
729
1019
  }
730
- writer.writeEndElement();
731
- return writer.getXmlString();
1020
+ builder.writeEndElement("Root");
1021
+ return builder.toString();
732
1022
  }
733
- function writeParameters(writer, iterator) {
734
- if (iterator) {
735
- writer.writeStartElement("Parameters");
736
- for (const parameter of iterator) {
737
- writer.writeStartElement("Parameter", { attributes: { id: parameter.id } });
738
- if (parameter.type !== void 0) writer.writeAttribute("type", parameter.type);
739
- if (parameter.value !== void 0) if (typeof parameter.value === "string") writer.writeAttribute("value", parameter.value);
740
- else if (parameter.value instanceof Date) writer.writeAttribute("value", dateToString(parameter.value, parameter.type));
741
- else if (parameter.value instanceof Uint8Array) writer.writeAttribute("value", uint8ArrayToBase64(parameter.value));
742
- else writer.writeAttribute("value", String(parameter.value));
743
- writer.writeEndElement();
1023
+ function writeParameters(builder, parameters) {
1024
+ if (parameters) {
1025
+ builder.writeStartElement("Parameters");
1026
+ for (const parameter of parameters) {
1027
+ const attrs = { id: parameter.id };
1028
+ if (parameter.type !== void 0) attrs.type = parameter.type;
1029
+ if (parameter.value !== void 0) if (typeof parameter.value === "string") attrs.value = parameter.value;
1030
+ else if (parameter.value instanceof Date) attrs.value = dateToString(parameter.value, parameter.type);
1031
+ else if (parameter.value instanceof Uint8Array) attrs.value = uint8ArrayToBase64(parameter.value);
1032
+ else attrs.value = String(parameter.value);
1033
+ builder.writeStartElement("Parameter", attrs, true);
744
1034
  }
745
- writer.writeEndElement();
1035
+ builder.writeEndElement("Parameters");
746
1036
  }
747
1037
  }
748
- function writeDataset(writer, dataset) {
749
- writer.writeStartElement("Dataset", { attributes: { id: dataset.id } });
1038
+ function writeDataset(builder, dataset) {
1039
+ builder.writeStartElement("Dataset", { id: dataset.id });
750
1040
  if (dataset.constColumnSize() > 0 || dataset.columnSize() > 0) {
751
- writer.writeStartElement("ColumnInfo");
752
- for (const constCol of dataset.iterConstColumns()) writer.writeStartElement("ConstColumn", {
753
- attributes: {
754
- id: constCol.id,
755
- size: String(constCol.size),
756
- type: constCol.type,
757
- value: constCol.value !== void 0 ? String(constCol.value) : ""
758
- },
759
- selfClosing: true
760
- });
761
- for (const col of dataset.iterColumns()) writer.writeStartElement("Column", {
762
- attributes: {
763
- id: col.id,
764
- size: String(col.size),
765
- type: col.type
766
- },
767
- selfClosing: true
768
- });
769
- writer.writeEndElement();
1041
+ builder.writeStartElement("ColumnInfo");
1042
+ for (const constCol of dataset.getConstColumns()) builder.writeStartElement("ConstColumn", {
1043
+ id: constCol.id,
1044
+ size: String(constCol.size),
1045
+ type: constCol.type,
1046
+ value: constCol.value !== void 0 ? String(constCol.value) : ""
1047
+ }, true);
1048
+ for (const col of dataset.getColumns()) builder.writeStartElement("Column", {
1049
+ id: col.id,
1050
+ size: String(col.size),
1051
+ type: col.type
1052
+ }, true);
1053
+ builder.writeEndElement("ColumnInfo");
770
1054
  }
771
- writer.writeStartElement("Rows");
772
- for (const row of dataset.iterRows()) {
773
- if (row.type) writer.writeStartElement("Row", { attributes: { type: row.type } });
774
- else writer.writeStartElement("Row");
775
- for (const col of row.cols) writeColumn(writer, dataset, col);
1055
+ builder.writeStartElement("Rows");
1056
+ for (const row of dataset.getRows()) {
1057
+ if (row.type) builder.writeStartElement("Row", { type: row.type });
1058
+ else builder.writeStartElement("Row");
1059
+ for (const col of row.cols) writeColumn(builder, dataset, col);
776
1060
  if (row.orgRow && row.orgRow.length > 0) {
777
- writer.writeStartElement("OrgRow");
778
- for (const orgCol of row.orgRow) writeColumn(writer, dataset, orgCol);
779
- writer.writeEndElement();
1061
+ builder.writeStartElement("OrgRow");
1062
+ for (const orgCol of row.orgRow) writeColumn(builder, dataset, orgCol);
1063
+ builder.writeEndElement("OrgRow");
780
1064
  }
781
- writer.writeEndElement();
1065
+ builder.writeEndElement("Row");
782
1066
  }
783
- writer.writeEndElement();
784
- writer.writeEndElement();
1067
+ builder.writeEndElement("Rows");
1068
+ builder.writeEndElement("Dataset");
785
1069
  }
786
- function writeColumn(writer, dataset, col) {
1070
+ function writeColumn(builder, dataset, col) {
787
1071
  if (col.value !== void 0 && col.value !== null) {
788
1072
  const colInfo = dataset.getColumnInfo(col.id);
789
- writer.writeStartElement("Col", { attributes: { id: col.id } });
790
- writer.writeCharacters(convertToString(col.value, colInfo.type));
791
- writer.writeEndElement();
792
- } else writer.writeStartElement("Col", {
793
- attributes: { id: col.id },
794
- selfClosing: true
795
- });
1073
+ builder.writeElementWithText("Col", { id: col.id }, convertToString(col.value, colInfo.type));
1074
+ } else builder.writeStartElement("Col", { id: col.id }, true);
1075
+ }
1076
+ //#endregion
1077
+ //#region src/schema.ts
1078
+ const defaultSizes = {
1079
+ STRING: 256,
1080
+ INT: 10,
1081
+ FLOAT: 20,
1082
+ DECIMAL: 20,
1083
+ BIGDECIMAL: 30,
1084
+ DATE: 8,
1085
+ DATETIME: 17,
1086
+ TIME: 6,
1087
+ BLOB: 1e3
1088
+ };
1089
+ function column(type, options = {}) {
1090
+ return {
1091
+ kind: "xapi-column",
1092
+ type,
1093
+ size: options.size ?? defaultSizes[type],
1094
+ optional: options.optional ?? false
1095
+ };
1096
+ }
1097
+ function defineDataset(columns) {
1098
+ return {
1099
+ kind: "xapi-dataset",
1100
+ columns
1101
+ };
1102
+ }
1103
+ function defineRoot(definition) {
1104
+ return {
1105
+ kind: "xapi-root",
1106
+ datasets: definition.datasets,
1107
+ parameters: definition.parameters ?? {}
1108
+ };
1109
+ }
1110
+ function defineOperation(definition) {
1111
+ return {
1112
+ kind: "xapi-operation",
1113
+ ...definition
1114
+ };
1115
+ }
1116
+ const xapi = {
1117
+ string: (options) => column("STRING", options),
1118
+ int: (options) => column("INT", options),
1119
+ float: (options) => column("FLOAT", options),
1120
+ decimal: (options) => column("DECIMAL", options),
1121
+ bigdecimal: (options) => column("BIGDECIMAL", options),
1122
+ date: (options) => column("DATE", options),
1123
+ datetime: (options) => column("DATETIME", options),
1124
+ time: (options) => column("TIME", options),
1125
+ blob: (options) => column("BLOB", options),
1126
+ dataset: defineDataset,
1127
+ root: defineRoot,
1128
+ operation: defineOperation
1129
+ };
1130
+ function encodeRoot(schema, value) {
1131
+ const root = new XapiRoot();
1132
+ for (const [id, parameterSchema] of Object.entries(schema.parameters)) {
1133
+ const parameterValue = value.parameters[id];
1134
+ if (parameterValue !== void 0 || !parameterSchema.optional) root.addParameter({
1135
+ id,
1136
+ type: parameterSchema.type,
1137
+ value: parameterValue
1138
+ });
1139
+ }
1140
+ for (const [datasetId, datasetSchema] of Object.entries(schema.datasets)) {
1141
+ const dataset = new Dataset(datasetId);
1142
+ for (const [id, columnSchema] of Object.entries(datasetSchema.columns)) dataset.addColumn({
1143
+ id,
1144
+ type: columnSchema.type,
1145
+ size: columnSchema.size
1146
+ });
1147
+ const rows = value.datasets[datasetId];
1148
+ for (const row of rows) {
1149
+ const rowIndex = dataset.newRow();
1150
+ for (const id of Object.keys(datasetSchema.columns)) dataset.setColumn(rowIndex, id, row[id]);
1151
+ }
1152
+ root.addDataset(dataset);
1153
+ }
1154
+ return root;
1155
+ }
1156
+ function decodeRoot(schema, root) {
1157
+ const parameters = {};
1158
+ for (const id of Object.keys(schema.parameters)) {
1159
+ const value = root.getParameter(id)?.value;
1160
+ if (value !== void 0) parameters[id] = value;
1161
+ }
1162
+ const datasets = {};
1163
+ for (const [datasetId, datasetSchema] of Object.entries(schema.datasets)) {
1164
+ const dataset = root.getDataset(datasetId);
1165
+ if (!dataset) {
1166
+ datasets[datasetId] = [];
1167
+ continue;
1168
+ }
1169
+ const constants = Object.fromEntries(dataset.getConstColumns().map(({ id, value }) => [id, value]));
1170
+ datasets[datasetId] = dataset.getRows().map((row) => {
1171
+ const value = { ...constants };
1172
+ for (const column of row.cols) if (column.id in datasetSchema.columns) value[column.id] = column.value;
1173
+ return value;
1174
+ });
1175
+ }
1176
+ return {
1177
+ parameters,
1178
+ datasets
1179
+ };
796
1180
  }
797
-
798
1181
  //#endregion
799
1182
  exports.ColumnTypeError = ColumnTypeError;
800
1183
  exports.Dataset = Dataset;
@@ -802,6 +1185,7 @@ exports.InvalidXmlError = InvalidXmlError;
802
1185
  exports.NexaVersion = NexaVersion;
803
1186
  exports.StringWritableStream = StringWritableStream;
804
1187
  exports.XapiRoot = XapiRoot;
1188
+ exports.XmlStringBuilder = XmlStringBuilder;
805
1189
  exports.XplatformVersion = XplatformVersion;
806
1190
  exports._unescapeXml = _unescapeXml;
807
1191
  exports.arrayBufferToString = arrayBufferToString;
@@ -810,13 +1194,21 @@ exports.columnType = columnType;
810
1194
  exports.convertToColumnType = convertToColumnType;
811
1195
  exports.convertToString = convertToString;
812
1196
  exports.dateToString = dateToString;
1197
+ exports.decodeRoot = decodeRoot;
1198
+ exports.defineDataset = defineDataset;
1199
+ exports.defineOperation = defineOperation;
1200
+ exports.defineRoot = defineRoot;
1201
+ exports.encodeControlChars = encodeControlChars;
1202
+ exports.encodeRoot = encodeRoot;
1203
+ exports.escapeXml = escapeXml;
813
1204
  exports.initXapi = initXapi;
814
1205
  exports.isXapiRoot = isXapiRoot;
815
1206
  exports.makeParseEntities = makeParseEntities;
816
- exports.makeWriterEntities = makeWriterEntities;
817
1207
  exports.parse = parse;
1208
+ exports.parseXml = parseXml;
818
1209
  exports.rowType = rowType;
819
1210
  exports.stringToDate = stringToDate;
820
1211
  exports.stringToReadableStream = stringToReadableStream;
821
1212
  exports.uint8ArrayToBase64 = uint8ArrayToBase64;
822
- exports.write = write;
1213
+ exports.write = write;
1214
+ exports.xapi = xapi;