@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.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.1",
|
|
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",
|