@xapi-js/core 1.3.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/LICENSE +21 -21
- package/README.md +21 -1
- 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/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022 Robert Soriano <https://github.com/wobsoriano>
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Robert Soriano <https://github.com/wobsoriano>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -25,6 +25,26 @@ deno add @xapi-ts/core
|
|
|
25
25
|
|
|
26
26
|
`@xapi-ts/core` provides the fundamental classes and functions for working with X-API data.
|
|
27
27
|
|
|
28
|
+
### Typed schemas
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { InferRoot, xapi } from '@xapi-js/core';
|
|
32
|
+
|
|
33
|
+
const schema = xapi.root({
|
|
34
|
+
parameters: { ErrorCode: xapi.int() },
|
|
35
|
+
datasets: {
|
|
36
|
+
users: xapi.dataset({
|
|
37
|
+
id: xapi.int(),
|
|
38
|
+
balance: xapi.bigdecimal(),
|
|
39
|
+
name: xapi.string({ size: 100 }),
|
|
40
|
+
photo: xapi.blob({ optional: true }),
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
type Payload = InferRoot<typeof schema>;
|
|
46
|
+
```
|
|
47
|
+
|
|
28
48
|
### Parsing X-API XML
|
|
29
49
|
|
|
30
50
|
You can parse an X-API XML string into an `XapiRoot` object:
|
|
@@ -198,4 +218,4 @@ xapi.addDataset(productsDataset);
|
|
|
198
218
|
|
|
199
219
|
const xmlOutput = write(xapi);
|
|
200
220
|
console.log('생성된 XML:\n', xmlOutput);
|
|
201
|
-
```
|
|
221
|
+
```
|
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 };
|
package/dist/index.d.ts
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 };
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,6 @@ var InvalidXmlError = class extends Error {
|
|
|
50
50
|
this.name = "InvalidXmlError";
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
-
|
|
54
53
|
//#endregion
|
|
55
54
|
//#region src/xapi-data.ts
|
|
56
55
|
/**
|
|
@@ -316,23 +315,30 @@ var Dataset = class {
|
|
|
316
315
|
return this.rows.length;
|
|
317
316
|
}
|
|
318
317
|
};
|
|
319
|
-
|
|
320
318
|
//#endregion
|
|
321
319
|
//#region src/utils.ts
|
|
322
320
|
function arrayBufferToString(buffer) {
|
|
323
321
|
return new TextDecoder().decode(buffer);
|
|
324
322
|
}
|
|
325
323
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
324
|
+
* Pre-defined XML entities for parsing, including control characters.
|
|
325
|
+
* Static array to avoid repeated allocation.
|
|
328
326
|
*/
|
|
329
|
-
|
|
330
|
-
const entities
|
|
331
|
-
for (let i = 1; i <= 32; i++) entities
|
|
327
|
+
const PARSE_ENTITIES = (() => {
|
|
328
|
+
const entities = [];
|
|
329
|
+
for (let i = 1; i <= 32; i++) entities.push({
|
|
332
330
|
entity: `&#${i};`,
|
|
333
331
|
value: String.fromCharCode(i)
|
|
334
332
|
});
|
|
335
|
-
return entities
|
|
333
|
+
return entities;
|
|
334
|
+
})();
|
|
335
|
+
/**
|
|
336
|
+
* Creates an array of entities for parsing XML, including control characters.
|
|
337
|
+
* @deprecated Use PARSE_ENTITIES directly for better performance
|
|
338
|
+
* @returns An array of objects, each with an `entity` (e.g., "") and its `value` (e.g., "\x01").
|
|
339
|
+
*/
|
|
340
|
+
function makeParseEntities() {
|
|
341
|
+
return PARSE_ENTITIES.slice();
|
|
336
342
|
}
|
|
337
343
|
/**
|
|
338
344
|
* Converts a Base64 string to a Uint8Array.
|
|
@@ -461,10 +467,10 @@ function stringToReadableStream(str) {
|
|
|
461
467
|
function convertToColumnType(value, type) {
|
|
462
468
|
switch (type) {
|
|
463
469
|
case "INT":
|
|
464
|
-
case "BIGDECIMAL":
|
|
465
470
|
const intValue = parseInt(value, 10);
|
|
466
471
|
return isNaN(intValue) ? value : intValue;
|
|
467
472
|
case "FLOAT":
|
|
473
|
+
case "BIGDECIMAL":
|
|
468
474
|
const floatValue = parseFloat(value);
|
|
469
475
|
return isNaN(floatValue) ? value : floatValue;
|
|
470
476
|
case "DECIMAL":
|
|
@@ -502,14 +508,21 @@ function convertToString(value, type) {
|
|
|
502
508
|
default: return String(value);
|
|
503
509
|
}
|
|
504
510
|
}
|
|
505
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Pre-compiled regex for control character entities.
|
|
513
|
+
* Created once to avoid repeated compilation.
|
|
514
|
+
*/
|
|
515
|
+
const CONTROL_CHAR_ENTITY_REGEX = new RegExp(PARSE_ENTITIES.map((e) => e.entity.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
|
|
516
|
+
/**
|
|
517
|
+
* Map for fast entity lookup during decoding.
|
|
518
|
+
*/
|
|
519
|
+
const ENTITY_MAP = new Map(PARSE_ENTITIES.map((e) => [e.entity, e.value]));
|
|
506
520
|
function _unescapeXml(str) {
|
|
507
521
|
if (!str) return str;
|
|
522
|
+
if (str.indexOf("&") === -1) return str;
|
|
508
523
|
let result = str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&/g, "&");
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const entity = entities.find((e) => e.entity === match);
|
|
512
|
-
return entity ? entity.value : match;
|
|
524
|
+
result = result.replace(CONTROL_CHAR_ENTITY_REGEX, (match) => {
|
|
525
|
+
return ENTITY_MAP.get(match) || match;
|
|
513
526
|
});
|
|
514
527
|
return result;
|
|
515
528
|
}
|
|
@@ -620,6 +633,18 @@ function parseXml(xml) {
|
|
|
620
633
|
return new XapiXmlParser(xml).parse();
|
|
621
634
|
}
|
|
622
635
|
/**
|
|
636
|
+
* Character codes for fast comparison
|
|
637
|
+
*/
|
|
638
|
+
const CHAR_SPACE = 32;
|
|
639
|
+
const CHAR_TAB = 9;
|
|
640
|
+
const CHAR_LF = 10;
|
|
641
|
+
const CHAR_CR = 13;
|
|
642
|
+
const CHAR_GT = 62;
|
|
643
|
+
const CHAR_SLASH = 47;
|
|
644
|
+
const CHAR_EQUALS = 61;
|
|
645
|
+
const CHAR_QUOTE = 34;
|
|
646
|
+
const CHAR_APOS = 39;
|
|
647
|
+
/**
|
|
623
648
|
* A lightweight XML parser optimized for X-API structure.
|
|
624
649
|
*/
|
|
625
650
|
var XapiXmlParser = class {
|
|
@@ -635,12 +660,12 @@ var XapiXmlParser = class {
|
|
|
635
660
|
while (this.pos < this.length) {
|
|
636
661
|
this.skipWhitespace();
|
|
637
662
|
if (this.pos >= this.length) break;
|
|
638
|
-
if (this.
|
|
663
|
+
if (this.matches("<?xml")) {
|
|
639
664
|
this.skipUntil("?>");
|
|
640
665
|
this.pos += 2;
|
|
641
666
|
continue;
|
|
642
667
|
}
|
|
643
|
-
if (this.
|
|
668
|
+
if (this.matches("<!--")) {
|
|
644
669
|
this.skipUntil("-->");
|
|
645
670
|
this.pos += 3;
|
|
646
671
|
continue;
|
|
@@ -658,11 +683,15 @@ var XapiXmlParser = class {
|
|
|
658
683
|
if (this.current() === "/") return null;
|
|
659
684
|
const tagName = this.parseTagName();
|
|
660
685
|
if (!tagName) return null;
|
|
661
|
-
const node = {
|
|
686
|
+
const node = {
|
|
687
|
+
tagName,
|
|
688
|
+
attributes: void 0,
|
|
689
|
+
children: void 0
|
|
690
|
+
};
|
|
662
691
|
const attributes = this.parseAttributes();
|
|
663
692
|
if (attributes && Object.keys(attributes).length > 0) node.attributes = attributes;
|
|
664
693
|
this.skipWhitespace();
|
|
665
|
-
if (this.
|
|
694
|
+
if (this.matches("/>")) {
|
|
666
695
|
this.pos += 2;
|
|
667
696
|
return node;
|
|
668
697
|
}
|
|
@@ -670,12 +699,12 @@ var XapiXmlParser = class {
|
|
|
670
699
|
const children = [];
|
|
671
700
|
let textContent = "";
|
|
672
701
|
while (this.pos < this.length) {
|
|
673
|
-
if (this.
|
|
702
|
+
if (this.matches("<![CDATA[")) {
|
|
674
703
|
const cdataText = this.parseCDATA();
|
|
675
704
|
if (cdataText !== null) textContent += cdataText;
|
|
676
705
|
continue;
|
|
677
706
|
}
|
|
678
|
-
if (this.
|
|
707
|
+
if (this.matches("</")) {
|
|
679
708
|
if (textContent) children.push(textContent);
|
|
680
709
|
this.pos += 2;
|
|
681
710
|
this.skipUntil(">");
|
|
@@ -698,21 +727,20 @@ var XapiXmlParser = class {
|
|
|
698
727
|
return node;
|
|
699
728
|
}
|
|
700
729
|
parseTagName() {
|
|
701
|
-
|
|
730
|
+
const start = this.pos;
|
|
702
731
|
while (this.pos < this.length) {
|
|
703
|
-
const
|
|
704
|
-
if (
|
|
705
|
-
name += ch;
|
|
732
|
+
const code = this.currentCharCode();
|
|
733
|
+
if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
706
734
|
this.pos++;
|
|
707
735
|
}
|
|
708
|
-
return
|
|
736
|
+
return this.xml.substring(start, this.pos);
|
|
709
737
|
}
|
|
710
738
|
parseAttributes() {
|
|
711
739
|
const attrs = {};
|
|
712
740
|
while (this.pos < this.length) {
|
|
713
741
|
this.skipWhitespace();
|
|
714
742
|
const ch = this.current();
|
|
715
|
-
if (ch === ">" || ch === "/" || this.
|
|
743
|
+
if (ch === ">" || ch === "/" || this.matches("/>")) break;
|
|
716
744
|
const attrName = this.parseAttributeName();
|
|
717
745
|
if (!attrName) break;
|
|
718
746
|
this.skipWhitespace();
|
|
@@ -723,74 +751,95 @@ var XapiXmlParser = class {
|
|
|
723
751
|
return Object.keys(attrs).length > 0 ? attrs : void 0;
|
|
724
752
|
}
|
|
725
753
|
parseAttributeName() {
|
|
726
|
-
|
|
754
|
+
const start = this.pos;
|
|
727
755
|
while (this.pos < this.length) {
|
|
728
|
-
const
|
|
729
|
-
if (
|
|
730
|
-
name += ch;
|
|
756
|
+
const code = this.currentCharCode();
|
|
757
|
+
if (code === CHAR_EQUALS || this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
731
758
|
this.pos++;
|
|
732
759
|
}
|
|
733
|
-
return
|
|
760
|
+
return this.xml.substring(start, this.pos);
|
|
734
761
|
}
|
|
735
762
|
parseAttributeValue() {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
763
|
+
const quoteCode = this.currentCharCode();
|
|
764
|
+
if (quoteCode !== CHAR_QUOTE && quoteCode !== CHAR_APOS) {
|
|
765
|
+
const start = this.pos;
|
|
739
766
|
while (this.pos < this.length) {
|
|
740
|
-
const
|
|
741
|
-
if (
|
|
742
|
-
value += ch;
|
|
767
|
+
const code = this.currentCharCode();
|
|
768
|
+
if (this.isWhitespace(code) || code === CHAR_GT || code === CHAR_SLASH) break;
|
|
743
769
|
this.pos++;
|
|
744
770
|
}
|
|
745
|
-
return
|
|
771
|
+
return this.xml.substring(start, this.pos);
|
|
746
772
|
}
|
|
747
773
|
this.pos++;
|
|
774
|
+
const start = this.pos;
|
|
748
775
|
while (this.pos < this.length) {
|
|
749
|
-
|
|
750
|
-
|
|
776
|
+
if (this.currentCharCode() === quoteCode) {
|
|
777
|
+
const value = this.xml.substring(start, this.pos);
|
|
751
778
|
this.pos++;
|
|
752
|
-
|
|
779
|
+
return value;
|
|
753
780
|
}
|
|
754
|
-
value += ch;
|
|
755
781
|
this.pos++;
|
|
756
782
|
}
|
|
757
|
-
return
|
|
783
|
+
return this.xml.substring(start, this.pos);
|
|
758
784
|
}
|
|
759
785
|
parseCDATA() {
|
|
760
|
-
if (this.
|
|
786
|
+
if (!this.matches("<![CDATA[")) return null;
|
|
761
787
|
this.pos += 9;
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
768
|
-
content += this.current();
|
|
769
|
-
this.pos++;
|
|
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;
|
|
770
793
|
}
|
|
794
|
+
const content = this.xml.substring(this.pos, endPos);
|
|
795
|
+
this.pos = endPos + 3;
|
|
771
796
|
return content;
|
|
772
797
|
}
|
|
773
798
|
skipWhitespace() {
|
|
774
799
|
while (this.pos < this.length) {
|
|
775
|
-
const
|
|
776
|
-
if (
|
|
800
|
+
const code = this.currentCharCode();
|
|
801
|
+
if (code !== CHAR_SPACE && code !== CHAR_TAB && code !== CHAR_LF && code !== CHAR_CR) break;
|
|
777
802
|
this.pos++;
|
|
778
803
|
}
|
|
779
804
|
}
|
|
805
|
+
isWhitespace(code) {
|
|
806
|
+
return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_LF || code === CHAR_CR;
|
|
807
|
+
}
|
|
780
808
|
skipUntil(target) {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
809
|
+
const pos = this.findString(target);
|
|
810
|
+
if (pos !== -1) this.pos = pos;
|
|
811
|
+
else this.pos = this.length;
|
|
785
812
|
}
|
|
786
813
|
current() {
|
|
787
814
|
return this.xml[this.pos];
|
|
788
815
|
}
|
|
789
|
-
|
|
790
|
-
return this.xml.
|
|
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;
|
|
791
841
|
}
|
|
792
842
|
};
|
|
793
|
-
|
|
794
843
|
//#endregion
|
|
795
844
|
//#region src/handler.ts
|
|
796
845
|
let _options = {
|
|
@@ -825,28 +874,31 @@ function parse(xml) {
|
|
|
825
874
|
if (typeof child === "string") continue;
|
|
826
875
|
const tagName = child.tagName;
|
|
827
876
|
if (tagName === "Parameters") parseParameters(child, xapiRoot);
|
|
828
|
-
else if (tagName === "
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const dataset = new Dataset(datasetId);
|
|
832
|
-
xapiRoot.addDataset(dataset);
|
|
833
|
-
const datasetChildren = child.children;
|
|
834
|
-
let columnInfoElement;
|
|
835
|
-
let rowsElement;
|
|
836
|
-
if (datasetChildren) for (let j = 0; j < datasetChildren.length; j++) {
|
|
837
|
-
const datasetChild = datasetChildren[j];
|
|
838
|
-
if (typeof datasetChild === "string") continue;
|
|
839
|
-
const childTagName = datasetChild.tagName;
|
|
840
|
-
if (childTagName === "ColumnInfo") columnInfoElement = datasetChild;
|
|
841
|
-
else if (childTagName === "Rows") rowsElement = datasetChild;
|
|
842
|
-
if (columnInfoElement && rowsElement) break;
|
|
843
|
-
}
|
|
844
|
-
parseColumnInfo(columnInfoElement, dataset);
|
|
845
|
-
parseRows(rowsElement, dataset);
|
|
846
|
-
}
|
|
877
|
+
else if (tagName === "Datasets") {
|
|
878
|
+
for (const datasetElement of child.children ?? []) if (typeof datasetElement !== "string" && datasetElement.tagName === "Dataset") parseDataset(datasetElement, xapiRoot);
|
|
879
|
+
} else if (tagName === "Dataset") parseDataset(child, xapiRoot);
|
|
847
880
|
}
|
|
848
881
|
return xapiRoot;
|
|
849
882
|
}
|
|
883
|
+
function parseDataset(child, xapiRoot) {
|
|
884
|
+
if (!child.attributes || !child.attributes.id) throw new InvalidXmlError("Dataset element must have an 'id' attribute");
|
|
885
|
+
const datasetId = child.attributes.id;
|
|
886
|
+
const dataset = new Dataset(datasetId);
|
|
887
|
+
xapiRoot.addDataset(dataset);
|
|
888
|
+
const datasetChildren = child.children;
|
|
889
|
+
let columnInfoElement;
|
|
890
|
+
let rowsElement;
|
|
891
|
+
if (datasetChildren) for (let j = 0; j < datasetChildren.length; j++) {
|
|
892
|
+
const datasetChild = datasetChildren[j];
|
|
893
|
+
if (typeof datasetChild === "string") continue;
|
|
894
|
+
const childTagName = datasetChild.tagName;
|
|
895
|
+
if (childTagName === "ColumnInfo") columnInfoElement = datasetChild;
|
|
896
|
+
else if (childTagName === "Rows") rowsElement = datasetChild;
|
|
897
|
+
if (columnInfoElement && rowsElement) break;
|
|
898
|
+
}
|
|
899
|
+
parseColumnInfo(columnInfoElement, dataset);
|
|
900
|
+
parseRows(rowsElement, dataset);
|
|
901
|
+
}
|
|
850
902
|
function parseValue(value, type = "STRING") {
|
|
851
903
|
value = _unescapeXml(value);
|
|
852
904
|
return _options.parseToTypes ? convertToColumnType(value, type) : value;
|
|
@@ -942,7 +994,7 @@ function parseParameters(parametersElement, xapiRoot) {
|
|
|
942
994
|
const attrs = p.attributes;
|
|
943
995
|
const id = attrs?.id;
|
|
944
996
|
const type = attrs?.type || "STRING";
|
|
945
|
-
const value = p.children?.[0];
|
|
997
|
+
const value = p.children?.[0] ?? attrs?.value;
|
|
946
998
|
xapiRoot.addParameter({
|
|
947
999
|
id,
|
|
948
1000
|
type,
|
|
@@ -1020,6 +1072,110 @@ function writeColumn(builder, dataset, col) {
|
|
|
1020
1072
|
builder.writeElementWithText("Col", { id: col.id }, convertToString(col.value, colInfo.type));
|
|
1021
1073
|
} else builder.writeStartElement("Col", { id: col.id }, true);
|
|
1022
1074
|
}
|
|
1023
|
-
|
|
1024
1075
|
//#endregion
|
|
1025
|
-
|
|
1076
|
+
//#region src/schema.ts
|
|
1077
|
+
const defaultSizes = {
|
|
1078
|
+
STRING: 256,
|
|
1079
|
+
INT: 10,
|
|
1080
|
+
FLOAT: 20,
|
|
1081
|
+
DECIMAL: 20,
|
|
1082
|
+
BIGDECIMAL: 30,
|
|
1083
|
+
DATE: 8,
|
|
1084
|
+
DATETIME: 17,
|
|
1085
|
+
TIME: 6,
|
|
1086
|
+
BLOB: 1e3
|
|
1087
|
+
};
|
|
1088
|
+
function column(type, options = {}) {
|
|
1089
|
+
return {
|
|
1090
|
+
kind: "xapi-column",
|
|
1091
|
+
type,
|
|
1092
|
+
size: options.size ?? defaultSizes[type],
|
|
1093
|
+
optional: options.optional ?? false
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function defineDataset(columns) {
|
|
1097
|
+
return {
|
|
1098
|
+
kind: "xapi-dataset",
|
|
1099
|
+
columns
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
function defineRoot(definition) {
|
|
1103
|
+
return {
|
|
1104
|
+
kind: "xapi-root",
|
|
1105
|
+
datasets: definition.datasets,
|
|
1106
|
+
parameters: definition.parameters ?? {}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
function defineOperation(definition) {
|
|
1110
|
+
return {
|
|
1111
|
+
kind: "xapi-operation",
|
|
1112
|
+
...definition
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
const xapi = {
|
|
1116
|
+
string: (options) => column("STRING", options),
|
|
1117
|
+
int: (options) => column("INT", options),
|
|
1118
|
+
float: (options) => column("FLOAT", options),
|
|
1119
|
+
decimal: (options) => column("DECIMAL", options),
|
|
1120
|
+
bigdecimal: (options) => column("BIGDECIMAL", options),
|
|
1121
|
+
date: (options) => column("DATE", options),
|
|
1122
|
+
datetime: (options) => column("DATETIME", options),
|
|
1123
|
+
time: (options) => column("TIME", options),
|
|
1124
|
+
blob: (options) => column("BLOB", options),
|
|
1125
|
+
dataset: defineDataset,
|
|
1126
|
+
root: defineRoot,
|
|
1127
|
+
operation: defineOperation
|
|
1128
|
+
};
|
|
1129
|
+
function encodeRoot(schema, value) {
|
|
1130
|
+
const root = new XapiRoot();
|
|
1131
|
+
for (const [id, parameterSchema] of Object.entries(schema.parameters)) {
|
|
1132
|
+
const parameterValue = value.parameters[id];
|
|
1133
|
+
if (parameterValue !== void 0 || !parameterSchema.optional) root.addParameter({
|
|
1134
|
+
id,
|
|
1135
|
+
type: parameterSchema.type,
|
|
1136
|
+
value: parameterValue
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
for (const [datasetId, datasetSchema] of Object.entries(schema.datasets)) {
|
|
1140
|
+
const dataset = new Dataset(datasetId);
|
|
1141
|
+
for (const [id, columnSchema] of Object.entries(datasetSchema.columns)) dataset.addColumn({
|
|
1142
|
+
id,
|
|
1143
|
+
type: columnSchema.type,
|
|
1144
|
+
size: columnSchema.size
|
|
1145
|
+
});
|
|
1146
|
+
const rows = value.datasets[datasetId];
|
|
1147
|
+
for (const row of rows) {
|
|
1148
|
+
const rowIndex = dataset.newRow();
|
|
1149
|
+
for (const id of Object.keys(datasetSchema.columns)) dataset.setColumn(rowIndex, id, row[id]);
|
|
1150
|
+
}
|
|
1151
|
+
root.addDataset(dataset);
|
|
1152
|
+
}
|
|
1153
|
+
return root;
|
|
1154
|
+
}
|
|
1155
|
+
function decodeRoot(schema, root) {
|
|
1156
|
+
const parameters = {};
|
|
1157
|
+
for (const id of Object.keys(schema.parameters)) {
|
|
1158
|
+
const value = root.getParameter(id)?.value;
|
|
1159
|
+
if (value !== void 0) parameters[id] = value;
|
|
1160
|
+
}
|
|
1161
|
+
const datasets = {};
|
|
1162
|
+
for (const [datasetId, datasetSchema] of Object.entries(schema.datasets)) {
|
|
1163
|
+
const dataset = root.getDataset(datasetId);
|
|
1164
|
+
if (!dataset) {
|
|
1165
|
+
datasets[datasetId] = [];
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
const constants = Object.fromEntries(dataset.getConstColumns().map(({ id, value }) => [id, value]));
|
|
1169
|
+
datasets[datasetId] = dataset.getRows().map((row) => {
|
|
1170
|
+
const value = { ...constants };
|
|
1171
|
+
for (const column of row.cols) if (column.id in datasetSchema.columns) value[column.id] = column.value;
|
|
1172
|
+
return value;
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
return {
|
|
1176
|
+
parameters,
|
|
1177
|
+
datasets
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
//#endregion
|
|
1181
|
+
export { ColumnTypeError, Dataset, InvalidXmlError, NexaVersion, StringWritableStream, XapiRoot, 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 };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xapi-js/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"author": "Clickin <josh87786@gmail.com>",
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.11.24",
|
|
32
|
-
"typescript": "^
|
|
32
|
+
"typescript": "^7.0.2"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown",
|