@xapi-js/core 1.3.0 → 1.4.1
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/LICENSE +21 -21
- package/README.md +65 -182
- package/dist/index.cjs +246 -84
- package/dist/index.d.cts +76 -1
- package/dist/index.d.ts +76 -1
- package/dist/index.js +239 -83
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region src/types.ts
|
|
3
3
|
/**
|
|
4
4
|
* Defines the possible types of rows in an X-API dataset (e.g., "insert", "update", "delete").
|
|
@@ -51,7 +51,6 @@ var InvalidXmlError = class extends Error {
|
|
|
51
51
|
this.name = "InvalidXmlError";
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
|
|
55
54
|
//#endregion
|
|
56
55
|
//#region src/xapi-data.ts
|
|
57
56
|
/**
|
|
@@ -317,23 +316,30 @@ var Dataset = class {
|
|
|
317
316
|
return this.rows.length;
|
|
318
317
|
}
|
|
319
318
|
};
|
|
320
|
-
|
|
321
319
|
//#endregion
|
|
322
320
|
//#region src/utils.ts
|
|
323
321
|
function arrayBufferToString(buffer) {
|
|
324
322
|
return new TextDecoder().decode(buffer);
|
|
325
323
|
}
|
|
326
324
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
325
|
+
* Pre-defined XML entities for parsing, including control characters.
|
|
326
|
+
* Static array to avoid repeated allocation.
|
|
329
327
|
*/
|
|
330
|
-
|
|
331
|
-
const entities
|
|
332
|
-
for (let i = 1; i <= 32; i++) entities
|
|
328
|
+
const PARSE_ENTITIES = (() => {
|
|
329
|
+
const entities = [];
|
|
330
|
+
for (let i = 1; i <= 32; i++) entities.push({
|
|
333
331
|
entity: `&#${i};`,
|
|
334
332
|
value: String.fromCharCode(i)
|
|
335
333
|
});
|
|
336
|
-
return entities
|
|
334
|
+
return entities;
|
|
335
|
+
})();
|
|
336
|
+
/**
|
|
337
|
+
* Creates an array of entities for parsing XML, including control characters.
|
|
338
|
+
* @deprecated Use PARSE_ENTITIES directly for better performance
|
|
339
|
+
* @returns An array of objects, each with an `entity` (e.g., "") and its `value` (e.g., "\x01").
|
|
340
|
+
*/
|
|
341
|
+
function makeParseEntities() {
|
|
342
|
+
return PARSE_ENTITIES.slice();
|
|
337
343
|
}
|
|
338
344
|
/**
|
|
339
345
|
* Converts a Base64 string to a Uint8Array.
|
|
@@ -462,10 +468,10 @@ function stringToReadableStream(str) {
|
|
|
462
468
|
function convertToColumnType(value, type) {
|
|
463
469
|
switch (type) {
|
|
464
470
|
case "INT":
|
|
465
|
-
case "BIGDECIMAL":
|
|
466
471
|
const intValue = parseInt(value, 10);
|
|
467
472
|
return isNaN(intValue) ? value : intValue;
|
|
468
473
|
case "FLOAT":
|
|
474
|
+
case "BIGDECIMAL":
|
|
469
475
|
const floatValue = parseFloat(value);
|
|
470
476
|
return isNaN(floatValue) ? value : floatValue;
|
|
471
477
|
case "DECIMAL":
|
|
@@ -503,14 +509,21 @@ function convertToString(value, type) {
|
|
|
503
509
|
default: return String(value);
|
|
504
510
|
}
|
|
505
511
|
}
|
|
506
|
-
|
|
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]));
|
|
507
521
|
function _unescapeXml(str) {
|
|
508
522
|
if (!str) return str;
|
|
523
|
+
if (str.indexOf("&") === -1) return str;
|
|
509
524
|
let result = str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&/g, "&");
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const entity = entities.find((e) => e.entity === match);
|
|
513
|
-
return entity ? entity.value : match;
|
|
525
|
+
result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => {
|
|
526
|
+
return ENTITY_MAP.get(match) || match;
|
|
514
527
|
});
|
|
515
528
|
return result;
|
|
516
529
|
}
|
|
@@ -621,6 +634,18 @@ function parseXml(xml) {
|
|
|
621
634
|
return new XapiXmlParser(xml).parse();
|
|
622
635
|
}
|
|
623
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
|
+
/**
|
|
624
649
|
* A lightweight XML parser optimized for X-API structure.
|
|
625
650
|
*/
|
|
626
651
|
var XapiXmlParser = class {
|
|
@@ -636,12 +661,12 @@ var XapiXmlParser = class {
|
|
|
636
661
|
while (this.pos < this.length) {
|
|
637
662
|
this.skipWhitespace();
|
|
638
663
|
if (this.pos >= this.length) break;
|
|
639
|
-
if (this.
|
|
664
|
+
if (this.matches("<?xml")) {
|
|
640
665
|
this.skipUntil("?>");
|
|
641
666
|
this.pos += 2;
|
|
642
667
|
continue;
|
|
643
668
|
}
|
|
644
|
-
if (this.
|
|
669
|
+
if (this.matches("<!--")) {
|
|
645
670
|
this.skipUntil("-->");
|
|
646
671
|
this.pos += 3;
|
|
647
672
|
continue;
|
|
@@ -659,11 +684,15 @@ var XapiXmlParser = class {
|
|
|
659
684
|
if (this.current() === "/") return null;
|
|
660
685
|
const tagName = this.parseTagName();
|
|
661
686
|
if (!tagName) return null;
|
|
662
|
-
const node = {
|
|
687
|
+
const node = {
|
|
688
|
+
tagName,
|
|
689
|
+
attributes: void 0,
|
|
690
|
+
children: void 0
|
|
691
|
+
};
|
|
663
692
|
const attributes = this.parseAttributes();
|
|
664
693
|
if (attributes && Object.keys(attributes).length > 0) node.attributes = attributes;
|
|
665
694
|
this.skipWhitespace();
|
|
666
|
-
if (this.
|
|
695
|
+
if (this.matches("/>")) {
|
|
667
696
|
this.pos += 2;
|
|
668
697
|
return node;
|
|
669
698
|
}
|
|
@@ -671,12 +700,12 @@ var XapiXmlParser = class {
|
|
|
671
700
|
const children = [];
|
|
672
701
|
let textContent = "";
|
|
673
702
|
while (this.pos < this.length) {
|
|
674
|
-
if (this.
|
|
703
|
+
if (this.matches("<![CDATA[")) {
|
|
675
704
|
const cdataText = this.parseCDATA();
|
|
676
705
|
if (cdataText !== null) textContent += cdataText;
|
|
677
706
|
continue;
|
|
678
707
|
}
|
|
679
|
-
if (this.
|
|
708
|
+
if (this.matches("</")) {
|
|
680
709
|
if (textContent) children.push(textContent);
|
|
681
710
|
this.pos += 2;
|
|
682
711
|
this.skipUntil(">");
|
|
@@ -699,21 +728,20 @@ var XapiXmlParser = class {
|
|
|
699
728
|
return node;
|
|
700
729
|
}
|
|
701
730
|
parseTagName() {
|
|
702
|
-
|
|
731
|
+
const start = this.pos;
|
|
703
732
|
while (this.pos < this.length) {
|
|
704
|
-
const
|
|
705
|
-
if (
|
|
706
|
-
name += ch;
|
|
733
|
+
const code = this.currentCharCode();
|
|
734
|
+
if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
707
735
|
this.pos++;
|
|
708
736
|
}
|
|
709
|
-
return
|
|
737
|
+
return this.xml.substring(start, this.pos);
|
|
710
738
|
}
|
|
711
739
|
parseAttributes() {
|
|
712
740
|
const attrs = {};
|
|
713
741
|
while (this.pos < this.length) {
|
|
714
742
|
this.skipWhitespace();
|
|
715
743
|
const ch = this.current();
|
|
716
|
-
if (ch === ">" || ch === "/" || this.
|
|
744
|
+
if (ch === ">" || ch === "/" || this.matches("/>")) break;
|
|
717
745
|
const attrName = this.parseAttributeName();
|
|
718
746
|
if (!attrName) break;
|
|
719
747
|
this.skipWhitespace();
|
|
@@ -724,74 +752,95 @@ var XapiXmlParser = class {
|
|
|
724
752
|
return Object.keys(attrs).length > 0 ? attrs : void 0;
|
|
725
753
|
}
|
|
726
754
|
parseAttributeName() {
|
|
727
|
-
|
|
755
|
+
const start = this.pos;
|
|
728
756
|
while (this.pos < this.length) {
|
|
729
|
-
const
|
|
730
|
-
if (
|
|
731
|
-
name += ch;
|
|
757
|
+
const code = this.currentCharCode();
|
|
758
|
+
if (code === CHAR_EQUALS || this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
732
759
|
this.pos++;
|
|
733
760
|
}
|
|
734
|
-
return
|
|
761
|
+
return this.xml.substring(start, this.pos);
|
|
735
762
|
}
|
|
736
763
|
parseAttributeValue() {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
764
|
+
const quoteCode = this.currentCharCode();
|
|
765
|
+
if (quoteCode !== CHAR_QUOTE && quoteCode !== CHAR_APOS) {
|
|
766
|
+
const start = this.pos;
|
|
740
767
|
while (this.pos < this.length) {
|
|
741
|
-
const
|
|
742
|
-
if (
|
|
743
|
-
value += ch;
|
|
768
|
+
const code = this.currentCharCode();
|
|
769
|
+
if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
744
770
|
this.pos++;
|
|
745
771
|
}
|
|
746
|
-
return
|
|
772
|
+
return this.xml.substring(start, this.pos);
|
|
747
773
|
}
|
|
748
774
|
this.pos++;
|
|
775
|
+
const start = this.pos;
|
|
749
776
|
while (this.pos < this.length) {
|
|
750
|
-
|
|
751
|
-
|
|
777
|
+
if (this.currentCharCode() === quoteCode) {
|
|
778
|
+
const value = this.xml.substring(start, this.pos);
|
|
752
779
|
this.pos++;
|
|
753
|
-
|
|
780
|
+
return value;
|
|
754
781
|
}
|
|
755
|
-
value += ch;
|
|
756
782
|
this.pos++;
|
|
757
783
|
}
|
|
758
|
-
return
|
|
784
|
+
return this.xml.substring(start, this.pos);
|
|
759
785
|
}
|
|
760
786
|
parseCDATA() {
|
|
761
|
-
if (this.
|
|
787
|
+
if (!this.matches("<![CDATA[")) return null;
|
|
762
788
|
this.pos += 9;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
}
|
|
769
|
-
content += this.current();
|
|
770
|
-
this.pos++;
|
|
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;
|
|
771
794
|
}
|
|
795
|
+
const content = this.xml.substring(this.pos, endPos);
|
|
796
|
+
this.pos = endPos + 3;
|
|
772
797
|
return content;
|
|
773
798
|
}
|
|
774
799
|
skipWhitespace() {
|
|
775
800
|
while (this.pos < this.length) {
|
|
776
|
-
const
|
|
777
|
-
if (
|
|
801
|
+
const code = this.currentCharCode();
|
|
802
|
+
if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
|
|
778
803
|
this.pos++;
|
|
779
804
|
}
|
|
780
805
|
}
|
|
806
|
+
isWhitespace(code) {
|
|
807
|
+
return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR;
|
|
808
|
+
}
|
|
781
809
|
skipUntil(target) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
810
|
+
const pos = this.findString(target);
|
|
811
|
+
if (pos !== -1) this.pos = pos;
|
|
812
|
+
else this.pos = this.length;
|
|
786
813
|
}
|
|
787
814
|
current() {
|
|
788
815
|
return this.xml[this.pos];
|
|
789
816
|
}
|
|
790
|
-
|
|
791
|
-
return this.xml.
|
|
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;
|
|
792
842
|
}
|
|
793
843
|
};
|
|
794
|
-
|
|
795
844
|
//#endregion
|
|
796
845
|
//#region src/handler.ts
|
|
797
846
|
let _options = {
|
|
@@ -826,28 +875,31 @@ function parse(xml) {
|
|
|
826
875
|
if (typeof child === "string") continue;
|
|
827
876
|
const tagName = child.tagName;
|
|
828
877
|
if (tagName === "Parameters") parseParameters(child, xapiRoot);
|
|
829
|
-
else if (tagName === "
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const dataset = new Dataset(datasetId);
|
|
833
|
-
xapiRoot.addDataset(dataset);
|
|
834
|
-
const datasetChildren = child.children;
|
|
835
|
-
let columnInfoElement;
|
|
836
|
-
let rowsElement;
|
|
837
|
-
if (datasetChildren) for (let j = 0; j < datasetChildren.length; j++) {
|
|
838
|
-
const datasetChild = datasetChildren[j];
|
|
839
|
-
if (typeof datasetChild === "string") continue;
|
|
840
|
-
const childTagName = datasetChild.tagName;
|
|
841
|
-
if (childTagName === "ColumnInfo") columnInfoElement = datasetChild;
|
|
842
|
-
else if (childTagName === "Rows") rowsElement = datasetChild;
|
|
843
|
-
if (columnInfoElement && rowsElement) break;
|
|
844
|
-
}
|
|
845
|
-
parseColumnInfo(columnInfoElement, dataset);
|
|
846
|
-
parseRows(rowsElement, dataset);
|
|
847
|
-
}
|
|
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);
|
|
848
881
|
}
|
|
849
882
|
return xapiRoot;
|
|
850
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
|
+
}
|
|
851
903
|
function parseValue(value, type = "STRING") {
|
|
852
904
|
value = _unescapeXml(value);
|
|
853
905
|
return _options.parseToTypes ? convertToColumnType(value, type) : value;
|
|
@@ -943,7 +995,7 @@ function parseParameters(parametersElement, xapiRoot) {
|
|
|
943
995
|
const attrs = p.attributes;
|
|
944
996
|
const id = attrs?.id;
|
|
945
997
|
const type = attrs?.type || "STRING";
|
|
946
|
-
const value = p.children?.[0];
|
|
998
|
+
const value = p.children?.[0] ?? attrs?.value;
|
|
947
999
|
xapiRoot.addParameter({
|
|
948
1000
|
id,
|
|
949
1001
|
type,
|
|
@@ -1021,7 +1073,111 @@ function writeColumn(builder, dataset, col) {
|
|
|
1021
1073
|
builder.writeElementWithText("Col", { id: col.id }, convertToString(col.value, colInfo.type));
|
|
1022
1074
|
} else builder.writeStartElement("Col", { id: col.id }, true);
|
|
1023
1075
|
}
|
|
1024
|
-
|
|
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
|
+
};
|
|
1180
|
+
}
|
|
1025
1181
|
//#endregion
|
|
1026
1182
|
exports.ColumnTypeError = ColumnTypeError;
|
|
1027
1183
|
exports.Dataset = Dataset;
|
|
@@ -1038,7 +1194,12 @@ exports.columnType = columnType;
|
|
|
1038
1194
|
exports.convertToColumnType = convertToColumnType;
|
|
1039
1195
|
exports.convertToString = convertToString;
|
|
1040
1196
|
exports.dateToString = dateToString;
|
|
1197
|
+
exports.decodeRoot = decodeRoot;
|
|
1198
|
+
exports.defineDataset = defineDataset;
|
|
1199
|
+
exports.defineOperation = defineOperation;
|
|
1200
|
+
exports.defineRoot = defineRoot;
|
|
1041
1201
|
exports.encodeControlChars = encodeControlChars;
|
|
1202
|
+
exports.encodeRoot = encodeRoot;
|
|
1042
1203
|
exports.escapeXml = escapeXml;
|
|
1043
1204
|
exports.initXapi = initXapi;
|
|
1044
1205
|
exports.isXapiRoot = isXapiRoot;
|
|
@@ -1049,4 +1210,5 @@ exports.rowType = rowType;
|
|
|
1049
1210
|
exports.stringToDate = stringToDate;
|
|
1050
1211
|
exports.stringToReadableStream = stringToReadableStream;
|
|
1051
1212
|
exports.uint8ArrayToBase64 = uint8ArrayToBase64;
|
|
1052
|
-
exports.write = write;
|
|
1213
|
+
exports.write = write;
|
|
1214
|
+
exports.xapi = xapi;
|
package/dist/index.d.cts
CHANGED
|
@@ -341,6 +341,7 @@ type XmlNode = {
|
|
|
341
341
|
declare function arrayBufferToString(buffer: ArrayBuffer): string;
|
|
342
342
|
/**
|
|
343
343
|
* Creates an array of entities for parsing XML, including control characters.
|
|
344
|
+
* @deprecated Use PARSE_ENTITIES directly for better performance
|
|
344
345
|
* @returns An array of objects, each with an `entity` (e.g., "") and its `value` (e.g., "\x01").
|
|
345
346
|
*/
|
|
346
347
|
declare function makeParseEntities(): {
|
|
@@ -468,4 +469,78 @@ declare class XmlStringBuilder {
|
|
|
468
469
|
*/
|
|
469
470
|
declare function parseXml(xml: string): XmlNode[];
|
|
470
471
|
//#endregion
|
|
471
|
-
|
|
472
|
+
//#region src/schema.d.ts
|
|
473
|
+
type XapiTypeMap = {
|
|
474
|
+
STRING: string;
|
|
475
|
+
INT: number;
|
|
476
|
+
FLOAT: number;
|
|
477
|
+
DECIMAL: number;
|
|
478
|
+
BIGDECIMAL: number;
|
|
479
|
+
DATE: Date;
|
|
480
|
+
DATETIME: Date;
|
|
481
|
+
TIME: Date;
|
|
482
|
+
BLOB: Uint8Array;
|
|
483
|
+
};
|
|
484
|
+
interface XapiColumnSchema<T extends ColumnType = ColumnType, Optional extends boolean = boolean> {
|
|
485
|
+
readonly kind: "xapi-column";
|
|
486
|
+
readonly type: T;
|
|
487
|
+
readonly size: number;
|
|
488
|
+
readonly optional: Optional;
|
|
489
|
+
}
|
|
490
|
+
type XapiColumns = Record<string, XapiColumnSchema>;
|
|
491
|
+
interface XapiDatasetSchema<Columns extends XapiColumns = XapiColumns> {
|
|
492
|
+
readonly kind: "xapi-dataset";
|
|
493
|
+
readonly columns: Columns;
|
|
494
|
+
}
|
|
495
|
+
type XapiDatasets = Record<string, XapiDatasetSchema>;
|
|
496
|
+
interface XapiRootSchema<Datasets extends XapiDatasets = XapiDatasets, Parameters extends XapiColumns = XapiColumns> {
|
|
497
|
+
readonly kind: "xapi-root";
|
|
498
|
+
readonly datasets: Datasets;
|
|
499
|
+
readonly parameters: Parameters;
|
|
500
|
+
}
|
|
501
|
+
interface XapiOperation<Request extends XapiRootSchema = XapiRootSchema, Response extends XapiRootSchema = XapiRootSchema> {
|
|
502
|
+
readonly kind: "xapi-operation";
|
|
503
|
+
readonly request: Request;
|
|
504
|
+
readonly response: Response;
|
|
505
|
+
}
|
|
506
|
+
type RequiredColumnKeys<Columns extends XapiColumns> = { [Key in keyof Columns]-?: Columns[Key]["optional"] extends true ? never : Key; }[keyof Columns];
|
|
507
|
+
type OptionalColumnKeys<Columns extends XapiColumns> = { [Key in keyof Columns]-?: Columns[Key]["optional"] extends true ? Key : never; }[keyof Columns];
|
|
508
|
+
type InferColumns<Columns extends XapiColumns> = { [Key in RequiredColumnKeys<Columns>]: XapiTypeMap[Columns[Key]["type"]]; } & { [Key in OptionalColumnKeys<Columns>]?: XapiTypeMap[Columns[Key]["type"]]; };
|
|
509
|
+
type InferDataset<Schema extends XapiDatasetSchema> = InferColumns<Schema["columns"]>[];
|
|
510
|
+
type InferRoot<Schema extends XapiRootSchema> = {
|
|
511
|
+
parameters: InferColumns<Schema["parameters"]>;
|
|
512
|
+
datasets: { [Key in keyof Schema["datasets"]]: InferDataset<Schema["datasets"][Key]>; };
|
|
513
|
+
};
|
|
514
|
+
type RequestOf<Operation extends XapiOperation> = InferRoot<Operation["request"]>;
|
|
515
|
+
type ResponseOf<Operation extends XapiOperation> = InferRoot<Operation["response"]>;
|
|
516
|
+
type ColumnOptions<Optional extends boolean> = {
|
|
517
|
+
size?: number;
|
|
518
|
+
optional?: Optional;
|
|
519
|
+
};
|
|
520
|
+
declare function defineDataset<const Columns extends XapiColumns>(columns: Columns): XapiDatasetSchema<Columns>;
|
|
521
|
+
declare function defineRoot<const Datasets extends XapiDatasets, const Parameters extends XapiColumns = Record<never, never>>(definition: {
|
|
522
|
+
datasets: Datasets;
|
|
523
|
+
parameters?: Parameters;
|
|
524
|
+
}): XapiRootSchema<Datasets, Parameters>;
|
|
525
|
+
declare function defineOperation<const Request extends XapiRootSchema, const Response extends XapiRootSchema>(definition: {
|
|
526
|
+
request: Request;
|
|
527
|
+
response: Response;
|
|
528
|
+
}): XapiOperation<Request, Response>;
|
|
529
|
+
declare const xapi: {
|
|
530
|
+
readonly string: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"STRING", Optional>;
|
|
531
|
+
readonly int: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"INT", Optional>;
|
|
532
|
+
readonly float: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"FLOAT", Optional>;
|
|
533
|
+
readonly decimal: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"DECIMAL", Optional>;
|
|
534
|
+
readonly bigdecimal: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"BIGDECIMAL", Optional>;
|
|
535
|
+
readonly date: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"DATE", Optional>;
|
|
536
|
+
readonly datetime: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"DATETIME", Optional>;
|
|
537
|
+
readonly time: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"TIME", Optional>;
|
|
538
|
+
readonly blob: <const Optional extends boolean = false>(options?: ColumnOptions<Optional>) => XapiColumnSchema<"BLOB", Optional>;
|
|
539
|
+
readonly dataset: typeof defineDataset;
|
|
540
|
+
readonly root: typeof defineRoot;
|
|
541
|
+
readonly operation: typeof defineOperation;
|
|
542
|
+
};
|
|
543
|
+
declare function encodeRoot<Schema extends XapiRootSchema>(schema: Schema, value: InferRoot<Schema>): XapiRoot;
|
|
544
|
+
declare function decodeRoot<Schema extends XapiRootSchema>(schema: Schema, root: XapiRoot): InferRoot<Schema>;
|
|
545
|
+
//#endregion
|
|
546
|
+
export { Col, Column, ColumnInfo, ColumnType, ColumnTypeError, ConstColumn, Dataset, InferColumns, InferDataset, InferRoot, InvalidXmlError, NexaVersion, Parameter, RequestOf, ResponseOf, Row, RowType, Rows, StringWritableStream, XapiColumnSchema, XapiColumns, XapiDatasetSchema, XapiDatasets, XapiOperation, XapiOptions, XapiParameters, XapiRoot, XapiRootSchema, XapiTypeMap, XapiValueType, XapiVersion, XmlNode, XmlStringBuilder, XplatformVersion, _unescapeXml, arrayBufferToString, base64ToUint8Array, columnType, convertToColumnType, convertToString, dateToString, decodeRoot, defineDataset, defineOperation, defineRoot, encodeControlChars, encodeRoot, escapeXml, initXapi, isXapiRoot, makeParseEntities, parse, parseXml, rowType, stringToDate, stringToReadableStream, uint8ArrayToBase64, write, xapi };
|