@xapi-js/core 1.4.1 → 1.5.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.
Files changed (3) hide show
  1. package/dist/index.cjs +167 -162
  2. package/dist/index.js +167 -162
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -522,9 +522,7 @@ function _unescapeXml(str) {
522
522
  if (!str) return str;
523
523
  if (str.indexOf("&") === -1) return str;
524
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;
527
- });
525
+ result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => ENTITY_MAP.get(match));
528
526
  return result;
529
527
  }
530
528
  function isXapiRoot(value) {
@@ -630,7 +628,7 @@ var XmlStringBuilder = class {
630
628
  * @returns An array of parsed XML nodes.
631
629
  */
632
630
  function parseXml(xml) {
633
- if (!xml || xml.trim() === "") return [];
631
+ if (!xml) return [];
634
632
  return new XapiXmlParser(xml).parse();
635
633
  }
636
634
  /**
@@ -645,6 +643,50 @@ const CHAR_SLASH = 47;
645
643
  const CHAR_EQUALS = 61;
646
644
  const CHAR_QUOTE = 34;
647
645
  const CHAR_APOS = 39;
646
+ /** Reuse the small, fixed X-API vocabulary instead of allocating a name per node. */
647
+ function xapiXmlName(xml, start, end) {
648
+ switch (end - start) {
649
+ case 2:
650
+ if (xml.startsWith("id", start)) return "id";
651
+ break;
652
+ case 3:
653
+ if (xml.startsWith("Col", start)) return "Col";
654
+ if (xml.startsWith("Row", start)) return "Row";
655
+ break;
656
+ case 4:
657
+ if (xml.startsWith("Root", start)) return "Root";
658
+ if (xml.startsWith("Rows", start)) return "Rows";
659
+ if (xml.startsWith("size", start)) return "size";
660
+ if (xml.startsWith("type", start)) return "type";
661
+ break;
662
+ case 5:
663
+ if (xml.startsWith("value", start)) return "value";
664
+ if (xml.startsWith("xmlns", start)) return "xmlns";
665
+ break;
666
+ case 6:
667
+ if (xml.startsWith("Column", start)) return "Column";
668
+ if (xml.startsWith("OrgRow", start)) return "OrgRow";
669
+ break;
670
+ case 7:
671
+ if (xml.startsWith("Dataset", start)) return "Dataset";
672
+ if (xml.startsWith("version", start)) return "version";
673
+ break;
674
+ case 8:
675
+ if (xml.startsWith("Datasets", start)) return "Datasets";
676
+ break;
677
+ case 9:
678
+ if (xml.startsWith("Parameter", start)) return "Parameter";
679
+ break;
680
+ case 10:
681
+ if (xml.startsWith("ColumnInfo", start)) return "ColumnInfo";
682
+ if (xml.startsWith("Parameters", start)) return "Parameters";
683
+ break;
684
+ case 11:
685
+ if (xml.startsWith("ConstColumn", start)) return "ConstColumn";
686
+ break;
687
+ }
688
+ return xml.substring(start, end);
689
+ }
648
690
  /**
649
691
  * A lightweight XML parser optimized for X-API structure.
650
692
  */
@@ -658,20 +700,26 @@ var XapiXmlParser = class {
658
700
  }
659
701
  parse() {
660
702
  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;
703
+ const xml = this.xml;
704
+ const length = this.length;
705
+ while (this.pos < length) {
706
+ while (this.pos < length) {
707
+ const code = xml.charCodeAt(this.pos);
708
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
709
+ this.pos++;
710
+ }
711
+ if (this.pos >= length) break;
712
+ if (xml.startsWith("<?xml", this.pos)) {
713
+ const end = xml.indexOf("?>", this.pos + 5);
714
+ this.pos = end < 0 ? length : end + 2;
667
715
  continue;
668
716
  }
669
- if (this.matches("<!--")) {
670
- this.skipUntil("-->");
671
- this.pos += 3;
717
+ if (xml.startsWith("<!--", this.pos)) {
718
+ const end = xml.indexOf("-->", this.pos + 4);
719
+ this.pos = end < 0 ? length : end + 3;
672
720
  continue;
673
721
  }
674
- if (this.current() === "<") {
722
+ if (xml.charCodeAt(this.pos) === 60) {
675
723
  const node = this.parseElement();
676
724
  if (node) nodes.push(node);
677
725
  } else this.pos++;
@@ -679,10 +727,17 @@ var XapiXmlParser = class {
679
727
  return nodes;
680
728
  }
681
729
  parseElement() {
682
- if (this.current() !== "<") return null;
730
+ const xml = this.xml;
731
+ const length = this.length;
683
732
  this.pos++;
684
- if (this.current() === "/") return null;
685
- const tagName = this.parseTagName();
733
+ if (xml.charCodeAt(this.pos) === CHAR_SLASH) return null;
734
+ const nameStart = this.pos;
735
+ while (this.pos < length) {
736
+ const code = xml.charCodeAt(this.pos);
737
+ if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
738
+ this.pos++;
739
+ }
740
+ const tagName = xapiXmlName(xml, nameStart, this.pos);
686
741
  if (!tagName) return null;
687
742
  const node = {
688
743
  tagName,
@@ -690,155 +745,110 @@ var XapiXmlParser = class {
690
745
  children: void 0
691
746
  };
692
747
  const attributes = this.parseAttributes();
693
- if (attributes && Object.keys(attributes).length > 0) node.attributes = attributes;
694
- this.skipWhitespace();
695
- if (this.matches("/>")) {
748
+ if (attributes) node.attributes = attributes;
749
+ if (xml.charCodeAt(this.pos) === CHAR_SLASH && xml.charCodeAt(this.pos + 1) === CHAR_GT) {
696
750
  this.pos += 2;
697
751
  return node;
698
752
  }
699
- if (this.current() === ">") this.pos++;
700
- const children = [];
753
+ if (xml.charCodeAt(this.pos) === CHAR_GT) this.pos++;
754
+ let children;
701
755
  let textContent = "";
702
- while (this.pos < this.length) {
703
- if (this.matches("<![CDATA[")) {
704
- const cdataText = this.parseCDATA();
705
- if (cdataText !== null) textContent += cdataText;
756
+ while (this.pos < length) {
757
+ const markup = xml.indexOf("<", this.pos);
758
+ if (markup < 0) {
759
+ this.pos = length;
760
+ break;
761
+ }
762
+ if (markup > this.pos) {
763
+ const text = xml.substring(this.pos, markup);
764
+ textContent = textContent ? textContent + text : text;
765
+ this.pos = markup;
766
+ }
767
+ if (xml.startsWith("<![CDATA[", this.pos)) {
768
+ const start = this.pos + 9;
769
+ const end = xml.indexOf("]]>", start);
770
+ if (end < 0) {
771
+ textContent += xml.substring(start);
772
+ this.pos = length;
773
+ break;
774
+ }
775
+ textContent += xml.substring(start, end);
776
+ this.pos = end + 3;
706
777
  continue;
707
778
  }
708
- if (this.matches("</")) {
709
- if (textContent) children.push(textContent);
779
+ if (xml.charCodeAt(this.pos + 1) === CHAR_SLASH) {
780
+ if (textContent && (!children || textContent.trim())) (children ??= []).push(textContent);
710
781
  this.pos += 2;
711
- this.skipUntil(">");
712
- this.pos++;
782
+ const end = xml.indexOf(">", this.pos);
783
+ this.pos = end < 0 ? length : end + 1;
713
784
  break;
714
785
  }
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++;
786
+ if (xml.startsWith("<!--", this.pos)) {
787
+ const end = xml.indexOf("-->", this.pos + 4);
788
+ this.pos = end < 0 ? length : end + 3;
789
+ continue;
725
790
  }
791
+ if (textContent && textContent.trim()) (children ??= []).push(textContent);
792
+ textContent = "";
793
+ const child = this.parseElement();
794
+ if (child) (children ??= []).push(child);
726
795
  }
727
- if (children.length > 0) node.children = children;
796
+ if (children) node.children = children;
728
797
  return node;
729
798
  }
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
799
  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();
800
+ const xml = this.xml;
801
+ const length = this.length;
802
+ let attrs;
803
+ while (this.pos < length) {
804
+ while (this.pos < length) {
805
+ const code = xml.charCodeAt(this.pos);
806
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
807
+ this.pos++;
808
+ }
809
+ const code = xml.charCodeAt(this.pos);
810
+ if (code === CHAR_GT || code === CHAR_SLASH) break;
811
+ const nameStart = this.pos;
812
+ while (this.pos < length) {
813
+ const code = xml.charCodeAt(this.pos);
814
+ if (code === CHAR_EQUALS || code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
815
+ this.pos++;
816
+ }
817
+ const attrName = xapiXmlName(xml, nameStart, this.pos);
746
818
  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;
819
+ while (this.pos < length) {
820
+ const code = xml.charCodeAt(this.pos);
821
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
770
822
  this.pos++;
771
823
  }
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);
824
+ if (xml.charCodeAt(this.pos) === CHAR_EQUALS) this.pos++;
825
+ while (this.pos < length) {
826
+ const code = xml.charCodeAt(this.pos);
827
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
779
828
  this.pos++;
780
- return value;
781
829
  }
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++;
830
+ const quote = xml.charCodeAt(this.pos);
831
+ let valueStart;
832
+ let valueEnd;
833
+ if (quote === CHAR_QUOTE || quote === CHAR_APOS) {
834
+ valueStart = ++this.pos;
835
+ valueEnd = xml.indexOf(quote === CHAR_QUOTE ? "\"" : "'", valueStart);
836
+ if (valueEnd < 0) {
837
+ valueEnd = length;
838
+ this.pos = length;
839
+ } else this.pos = valueEnd + 1;
840
+ } else {
841
+ valueStart = this.pos;
842
+ while (this.pos < length) {
843
+ const code = xml.charCodeAt(this.pos);
844
+ if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
845
+ this.pos++;
846
+ }
847
+ valueEnd = this.pos;
848
+ }
849
+ (attrs ??= {})[attrName] = xml.substring(valueStart, valueEnd);
804
850
  }
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;
851
+ return attrs;
842
852
  }
843
853
  };
844
854
  //#endregion
@@ -934,7 +944,6 @@ function parseColumnInfo(columnInfoElement, dataset) {
934
944
  }
935
945
  }
936
946
  function parseRows(rowsElement, dataset) {
937
- if (typeof rowsElement === "string") return;
938
947
  const rows = rowsElement?.children;
939
948
  if (!rows) return;
940
949
  const datasetRows = dataset.rows;
@@ -985,8 +994,6 @@ function parseRows(rowsElement, dataset) {
985
994
  }
986
995
  }
987
996
  function parseParameters(parametersElement, xapiRoot) {
988
- if (typeof parametersElement === "string") return;
989
- if (!parametersElement) return;
990
997
  const children = parametersElement.children;
991
998
  if (!children) return;
992
999
  for (let i = 0; i < children.length; i++) {
@@ -1021,19 +1028,17 @@ function write(root) {
1021
1028
  return builder.toString();
1022
1029
  }
1023
1030
  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);
1034
- }
1035
- builder.writeEndElement("Parameters");
1036
- }
1031
+ builder.writeStartElement("Parameters");
1032
+ for (const parameter of parameters) {
1033
+ const attrs = { id: parameter.id };
1034
+ if (parameter.type !== void 0) attrs.type = parameter.type;
1035
+ if (parameter.value !== void 0) if (typeof parameter.value === "string") attrs.value = parameter.value;
1036
+ else if (parameter.value instanceof Date) attrs.value = dateToString(parameter.value, parameter.type);
1037
+ else if (parameter.value instanceof Uint8Array) attrs.value = uint8ArrayToBase64(parameter.value);
1038
+ else attrs.value = String(parameter.value);
1039
+ builder.writeStartElement("Parameter", attrs, true);
1040
+ }
1041
+ builder.writeEndElement("Parameters");
1037
1042
  }
1038
1043
  function writeDataset(builder, dataset) {
1039
1044
  builder.writeStartElement("Dataset", { id: dataset.id });
package/dist/index.js CHANGED
@@ -521,9 +521,7 @@ function _unescapeXml(str) {
521
521
  if (!str) return str;
522
522
  if (str.indexOf("&") === -1) return str;
523
523
  let result = str.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&apos;/g, "'").replace(/&amp;/g, "&");
524
- result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => {
525
- return ENTITY_MAP.get(match) || match;
526
- });
524
+ result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => ENTITY_MAP.get(match));
527
525
  return result;
528
526
  }
529
527
  function isXapiRoot(value) {
@@ -629,7 +627,7 @@ var XmlStringBuilder = class {
629
627
  * @returns An array of parsed XML nodes.
630
628
  */
631
629
  function parseXml(xml) {
632
- if (!xml || xml.trim() === "") return [];
630
+ if (!xml) return [];
633
631
  return new XapiXmlParser(xml).parse();
634
632
  }
635
633
  /**
@@ -644,6 +642,50 @@ const CHAR_SLASH = 47;
644
642
  const CHAR_EQUALS = 61;
645
643
  const CHAR_QUOTE = 34;
646
644
  const CHAR_APOS = 39;
645
+ /** Reuse the small, fixed X-API vocabulary instead of allocating a name per node. */
646
+ function xapiXmlName(xml, start, end) {
647
+ switch (end - start) {
648
+ case 2:
649
+ if (xml.startsWith("id", start)) return "id";
650
+ break;
651
+ case 3:
652
+ if (xml.startsWith("Col", start)) return "Col";
653
+ if (xml.startsWith("Row", start)) return "Row";
654
+ break;
655
+ case 4:
656
+ if (xml.startsWith("Root", start)) return "Root";
657
+ if (xml.startsWith("Rows", start)) return "Rows";
658
+ if (xml.startsWith("size", start)) return "size";
659
+ if (xml.startsWith("type", start)) return "type";
660
+ break;
661
+ case 5:
662
+ if (xml.startsWith("value", start)) return "value";
663
+ if (xml.startsWith("xmlns", start)) return "xmlns";
664
+ break;
665
+ case 6:
666
+ if (xml.startsWith("Column", start)) return "Column";
667
+ if (xml.startsWith("OrgRow", start)) return "OrgRow";
668
+ break;
669
+ case 7:
670
+ if (xml.startsWith("Dataset", start)) return "Dataset";
671
+ if (xml.startsWith("version", start)) return "version";
672
+ break;
673
+ case 8:
674
+ if (xml.startsWith("Datasets", start)) return "Datasets";
675
+ break;
676
+ case 9:
677
+ if (xml.startsWith("Parameter", start)) return "Parameter";
678
+ break;
679
+ case 10:
680
+ if (xml.startsWith("ColumnInfo", start)) return "ColumnInfo";
681
+ if (xml.startsWith("Parameters", start)) return "Parameters";
682
+ break;
683
+ case 11:
684
+ if (xml.startsWith("ConstColumn", start)) return "ConstColumn";
685
+ break;
686
+ }
687
+ return xml.substring(start, end);
688
+ }
647
689
  /**
648
690
  * A lightweight XML parser optimized for X-API structure.
649
691
  */
@@ -657,20 +699,26 @@ var XapiXmlParser = class {
657
699
  }
658
700
  parse() {
659
701
  const nodes = [];
660
- while (this.pos < this.length) {
661
- this.skipWhitespace();
662
- if (this.pos >= this.length) break;
663
- if (this.matches("<?xml")) {
664
- this.skipUntil("?>");
665
- this.pos += 2;
702
+ const xml = this.xml;
703
+ const length = this.length;
704
+ while (this.pos < length) {
705
+ while (this.pos < length) {
706
+ const code = xml.charCodeAt(this.pos);
707
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
708
+ this.pos++;
709
+ }
710
+ if (this.pos >= length) break;
711
+ if (xml.startsWith("<?xml", this.pos)) {
712
+ const end = xml.indexOf("?>", this.pos + 5);
713
+ this.pos = end < 0 ? length : end + 2;
666
714
  continue;
667
715
  }
668
- if (this.matches("<!--")) {
669
- this.skipUntil("-->");
670
- this.pos += 3;
716
+ if (xml.startsWith("<!--", this.pos)) {
717
+ const end = xml.indexOf("-->", this.pos + 4);
718
+ this.pos = end < 0 ? length : end + 3;
671
719
  continue;
672
720
  }
673
- if (this.current() === "<") {
721
+ if (xml.charCodeAt(this.pos) === 60) {
674
722
  const node = this.parseElement();
675
723
  if (node) nodes.push(node);
676
724
  } else this.pos++;
@@ -678,10 +726,17 @@ var XapiXmlParser = class {
678
726
  return nodes;
679
727
  }
680
728
  parseElement() {
681
- if (this.current() !== "<") return null;
729
+ const xml = this.xml;
730
+ const length = this.length;
682
731
  this.pos++;
683
- if (this.current() === "/") return null;
684
- const tagName = this.parseTagName();
732
+ if (xml.charCodeAt(this.pos) === CHAR_SLASH) return null;
733
+ const nameStart = this.pos;
734
+ while (this.pos < length) {
735
+ const code = xml.charCodeAt(this.pos);
736
+ if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
737
+ this.pos++;
738
+ }
739
+ const tagName = xapiXmlName(xml, nameStart, this.pos);
685
740
  if (!tagName) return null;
686
741
  const node = {
687
742
  tagName,
@@ -689,155 +744,110 @@ var XapiXmlParser = class {
689
744
  children: void 0
690
745
  };
691
746
  const attributes = this.parseAttributes();
692
- if (attributes && Object.keys(attributes).length > 0) node.attributes = attributes;
693
- this.skipWhitespace();
694
- if (this.matches("/>")) {
747
+ if (attributes) node.attributes = attributes;
748
+ if (xml.charCodeAt(this.pos) === CHAR_SLASH && xml.charCodeAt(this.pos + 1) === CHAR_GT) {
695
749
  this.pos += 2;
696
750
  return node;
697
751
  }
698
- if (this.current() === ">") this.pos++;
699
- const children = [];
752
+ if (xml.charCodeAt(this.pos) === CHAR_GT) this.pos++;
753
+ let children;
700
754
  let textContent = "";
701
- while (this.pos < this.length) {
702
- if (this.matches("<![CDATA[")) {
703
- const cdataText = this.parseCDATA();
704
- if (cdataText !== null) textContent += cdataText;
755
+ while (this.pos < length) {
756
+ const markup = xml.indexOf("<", this.pos);
757
+ if (markup < 0) {
758
+ this.pos = length;
759
+ break;
760
+ }
761
+ if (markup > this.pos) {
762
+ const text = xml.substring(this.pos, markup);
763
+ textContent = textContent ? textContent + text : text;
764
+ this.pos = markup;
765
+ }
766
+ if (xml.startsWith("<![CDATA[", this.pos)) {
767
+ const start = this.pos + 9;
768
+ const end = xml.indexOf("]]>", start);
769
+ if (end < 0) {
770
+ textContent += xml.substring(start);
771
+ this.pos = length;
772
+ break;
773
+ }
774
+ textContent += xml.substring(start, end);
775
+ this.pos = end + 3;
705
776
  continue;
706
777
  }
707
- if (this.matches("</")) {
708
- if (textContent) children.push(textContent);
778
+ if (xml.charCodeAt(this.pos + 1) === CHAR_SLASH) {
779
+ if (textContent && (!children || textContent.trim())) (children ??= []).push(textContent);
709
780
  this.pos += 2;
710
- this.skipUntil(">");
711
- this.pos++;
781
+ const end = xml.indexOf(">", this.pos);
782
+ this.pos = end < 0 ? length : end + 1;
712
783
  break;
713
784
  }
714
- if (this.current() === "<") {
715
- if (textContent.trim()) {
716
- children.push(textContent);
717
- textContent = "";
718
- }
719
- const child = this.parseElement();
720
- if (child) children.push(child);
721
- } else {
722
- textContent += this.current();
723
- this.pos++;
785
+ if (xml.startsWith("<!--", this.pos)) {
786
+ const end = xml.indexOf("-->", this.pos + 4);
787
+ this.pos = end < 0 ? length : end + 3;
788
+ continue;
724
789
  }
790
+ if (textContent && textContent.trim()) (children ??= []).push(textContent);
791
+ textContent = "";
792
+ const child = this.parseElement();
793
+ if (child) (children ??= []).push(child);
725
794
  }
726
- if (children.length > 0) node.children = children;
795
+ if (children) node.children = children;
727
796
  return node;
728
797
  }
729
- parseTagName() {
730
- const start = this.pos;
731
- while (this.pos < this.length) {
732
- const code = this.currentCharCode();
733
- if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
734
- this.pos++;
735
- }
736
- return this.xml.substring(start, this.pos);
737
- }
738
798
  parseAttributes() {
739
- const attrs = {};
740
- while (this.pos < this.length) {
741
- this.skipWhitespace();
742
- const ch = this.current();
743
- if (ch === ">" || ch === "/" || this.matches("/>")) break;
744
- const attrName = this.parseAttributeName();
799
+ const xml = this.xml;
800
+ const length = this.length;
801
+ let attrs;
802
+ while (this.pos < length) {
803
+ while (this.pos < length) {
804
+ const code = xml.charCodeAt(this.pos);
805
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
806
+ this.pos++;
807
+ }
808
+ const code = xml.charCodeAt(this.pos);
809
+ if (code === CHAR_GT || code === CHAR_SLASH) break;
810
+ const nameStart = this.pos;
811
+ while (this.pos < length) {
812
+ const code = xml.charCodeAt(this.pos);
813
+ if (code === CHAR_EQUALS || code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
814
+ this.pos++;
815
+ }
816
+ const attrName = xapiXmlName(xml, nameStart, this.pos);
745
817
  if (!attrName) break;
746
- this.skipWhitespace();
747
- if (this.current() === "=") this.pos++;
748
- this.skipWhitespace();
749
- attrs[attrName] = this.parseAttributeValue();
750
- }
751
- return Object.keys(attrs).length > 0 ? attrs : void 0;
752
- }
753
- parseAttributeName() {
754
- const start = this.pos;
755
- while (this.pos < this.length) {
756
- const code = this.currentCharCode();
757
- if (code === CHAR_EQUALS || this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
758
- this.pos++;
759
- }
760
- return this.xml.substring(start, this.pos);
761
- }
762
- parseAttributeValue() {
763
- const quoteCode = this.currentCharCode();
764
- if (quoteCode !== CHAR_QUOTE && quoteCode !== CHAR_APOS) {
765
- const start = this.pos;
766
- while (this.pos < this.length) {
767
- const code = this.currentCharCode();
768
- if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
818
+ while (this.pos < length) {
819
+ const code = xml.charCodeAt(this.pos);
820
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
769
821
  this.pos++;
770
822
  }
771
- return this.xml.substring(start, this.pos);
772
- }
773
- this.pos++;
774
- const start = this.pos;
775
- while (this.pos < this.length) {
776
- if (this.currentCharCode() === quoteCode) {
777
- const value = this.xml.substring(start, this.pos);
823
+ if (xml.charCodeAt(this.pos) === CHAR_EQUALS) this.pos++;
824
+ while (this.pos < length) {
825
+ const code = xml.charCodeAt(this.pos);
826
+ if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
778
827
  this.pos++;
779
- return value;
780
828
  }
781
- this.pos++;
782
- }
783
- return this.xml.substring(start, this.pos);
784
- }
785
- parseCDATA() {
786
- if (!this.matches("<![CDATA[")) return null;
787
- this.pos += 9;
788
- const endPos = this.findString("]]>");
789
- if (endPos === -1) {
790
- const content = this.xml.substring(this.pos);
791
- this.pos = this.length;
792
- return content;
793
- }
794
- const content = this.xml.substring(this.pos, endPos);
795
- this.pos = endPos + 3;
796
- return content;
797
- }
798
- skipWhitespace() {
799
- while (this.pos < this.length) {
800
- const code = this.currentCharCode();
801
- if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
802
- this.pos++;
829
+ const quote = xml.charCodeAt(this.pos);
830
+ let valueStart;
831
+ let valueEnd;
832
+ if (quote === CHAR_QUOTE || quote === CHAR_APOS) {
833
+ valueStart = ++this.pos;
834
+ valueEnd = xml.indexOf(quote === CHAR_QUOTE ? "\"" : "'", valueStart);
835
+ if (valueEnd < 0) {
836
+ valueEnd = length;
837
+ this.pos = length;
838
+ } else this.pos = valueEnd + 1;
839
+ } else {
840
+ valueStart = this.pos;
841
+ while (this.pos < length) {
842
+ const code = xml.charCodeAt(this.pos);
843
+ if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR || code === CHAR_GT || code === CHAR_SLASH) break;
844
+ this.pos++;
845
+ }
846
+ valueEnd = this.pos;
847
+ }
848
+ (attrs ??= {})[attrName] = xml.substring(valueStart, valueEnd);
803
849
  }
804
- }
805
- isWhitespace(code) {
806
- return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR;
807
- }
808
- skipUntil(target) {
809
- const pos = this.findString(target);
810
- if (pos !== -1) this.pos = pos;
811
- else this.pos = this.length;
812
- }
813
- current() {
814
- return this.xml[this.pos];
815
- }
816
- currentCharCode() {
817
- return this.xml.charCodeAt(this.pos);
818
- }
819
- /**
820
- * Checks if the string at the given position matches the target string.
821
- * This avoids creating substring instances for comparison.
822
- * @param str - The target string to match
823
- * @param pos - The position to check from (default: current position)
824
- */
825
- matches(str, pos = this.pos) {
826
- const len = str.length;
827
- if (pos + len > this.length) return false;
828
- for (let i = 0; i < len; i++) if (this.xml.charCodeAt(pos + i) !== str.charCodeAt(i)) return false;
829
- return true;
830
- }
831
- /**
832
- * Finds the position of a target string starting from the given position.
833
- * @param str - The string to find
834
- * @param startPos - Starting position (default: current position)
835
- * @returns The position where the string is found, or -1 if not found
836
- */
837
- findString(str, startPos = this.pos) {
838
- const maxPos = this.length - str.length;
839
- for (let pos = startPos; pos <= maxPos; pos++) if (this.matches(str, pos)) return pos;
840
- return -1;
850
+ return attrs;
841
851
  }
842
852
  };
843
853
  //#endregion
@@ -933,7 +943,6 @@ function parseColumnInfo(columnInfoElement, dataset) {
933
943
  }
934
944
  }
935
945
  function parseRows(rowsElement, dataset) {
936
- if (typeof rowsElement === "string") return;
937
946
  const rows = rowsElement?.children;
938
947
  if (!rows) return;
939
948
  const datasetRows = dataset.rows;
@@ -984,8 +993,6 @@ function parseRows(rowsElement, dataset) {
984
993
  }
985
994
  }
986
995
  function parseParameters(parametersElement, xapiRoot) {
987
- if (typeof parametersElement === "string") return;
988
- if (!parametersElement) return;
989
996
  const children = parametersElement.children;
990
997
  if (!children) return;
991
998
  for (let i = 0; i < children.length; i++) {
@@ -1020,19 +1027,17 @@ function write(root) {
1020
1027
  return builder.toString();
1021
1028
  }
1022
1029
  function writeParameters(builder, parameters) {
1023
- if (parameters) {
1024
- builder.writeStartElement("Parameters");
1025
- for (const parameter of parameters) {
1026
- const attrs = { id: parameter.id };
1027
- if (parameter.type !== void 0) attrs.type = parameter.type;
1028
- if (parameter.value !== void 0) if (typeof parameter.value === "string") attrs.value = parameter.value;
1029
- else if (parameter.value instanceof Date) attrs.value = dateToString(parameter.value, parameter.type);
1030
- else if (parameter.value instanceof Uint8Array) attrs.value = uint8ArrayToBase64(parameter.value);
1031
- else attrs.value = String(parameter.value);
1032
- builder.writeStartElement("Parameter", attrs, true);
1033
- }
1034
- builder.writeEndElement("Parameters");
1035
- }
1030
+ builder.writeStartElement("Parameters");
1031
+ for (const parameter of parameters) {
1032
+ const attrs = { id: parameter.id };
1033
+ if (parameter.type !== void 0) attrs.type = parameter.type;
1034
+ if (parameter.value !== void 0) if (typeof parameter.value === "string") attrs.value = parameter.value;
1035
+ else if (parameter.value instanceof Date) attrs.value = dateToString(parameter.value, parameter.type);
1036
+ else if (parameter.value instanceof Uint8Array) attrs.value = uint8ArrayToBase64(parameter.value);
1037
+ else attrs.value = String(parameter.value);
1038
+ builder.writeStartElement("Parameter", attrs, true);
1039
+ }
1040
+ builder.writeEndElement("Parameters");
1036
1041
  }
1037
1042
  function writeDataset(builder, dataset) {
1038
1043
  builder.writeStartElement("Dataset", { id: dataset.id });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xapi-js/core",
3
3
  "type": "module",
4
- "version": "1.4.1",
4
+ "version": "1.5.0",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",