初始化环境文件
This commit is contained in:
759
node_modules/fast-xml-parser/CHANGELOG.md
generated
vendored
Normal file
759
node_modules/fast-xml-parser/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,759 @@
|
||||
<small>Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.</small>
|
||||
|
||||
Note: Due to some last quick changes on v4, detail of v4.5.3 & v4.5.4 are not updated here. v4.5.4x is the last tag of v4 in github repository. I'm extremely sorry for the confusion
|
||||
|
||||
**5.5.8 / 2026-03-20**
|
||||
- pass read only matcher in callback
|
||||
|
||||
**5.5.7 / 2026-03-19**
|
||||
- fix: entity expansion limits
|
||||
- update strnum package to 2.2.0
|
||||
|
||||
**5.5.6 / 2026-03-16**
|
||||
- update builder dependency
|
||||
- fix incorrect regex to replace \. in entity name
|
||||
- fix check for entitiy expansion for lastEntities and html entities too
|
||||
|
||||
**5.5.5 / 2026-03-13**
|
||||
- sanitize dangerous tag or attribute name
|
||||
- error on critical property name
|
||||
- support onDangerousProperty option
|
||||
|
||||
**5.5.4 / 2026-03-13**
|
||||
- declare Matcher & Expression as unknown so user is not forced to install path-expression-matcher
|
||||
|
||||
|
||||
**5.5.3 / 2026-03-11**
|
||||
- upgrade builder
|
||||
|
||||
**5.5.2 / 2026-03-11**
|
||||
- update dependency to fix typings
|
||||
|
||||
**5.5.1 / 2026-03-10**
|
||||
- fix dependency
|
||||
|
||||
**5.5.0 / 2026-03-10**
|
||||
- support path-expression-matcher
|
||||
- fix: stopNode should not be parsed
|
||||
- performance improvement for stopNode checking
|
||||
|
||||
**5.4.2 / 2026-03-03**
|
||||
- support maxEntityCount option
|
||||
|
||||
**5.4.1 / 2026-02-25**
|
||||
- fix (#785) unpairedTag node should not have tag content
|
||||
|
||||
**5.4.0 / 2026-02-25**
|
||||
- migrate to fast-xml-builder
|
||||
|
||||
**5.3.9 / 2026-02-25**
|
||||
- support strictReservedNames
|
||||
|
||||
**5.3.8 / 2026-02-25**
|
||||
- support maxNestedTags
|
||||
- handle non-array input for XML builder when preserveOrder is true (By [Angelo Coetzee](https://github.com/Angelopvtac))
|
||||
- save use of js properies
|
||||
|
||||
**5.3.7 / 2026-02-20**
|
||||
- fix typings for CJS (By [Corentin Girard](https://github.com/Drarig29))
|
||||
|
||||
|
||||
|
||||
**5.3.6 / 2026-02-14**
|
||||
- Improve security and performance of entity processing
|
||||
- new options `maxEntitySize`, `maxExpansionDepth`, `maxTotalExpansions`, `maxExpandedLength`, `allowedTags`,`tagFilter`
|
||||
- fast return when no edtity is present
|
||||
- improvement replacement logic to reduce number of calls
|
||||
|
||||
|
||||
**5.3.5 / 2026-02-08**
|
||||
- fix: Escape regex char in entity name
|
||||
- update strnum to 2.1.2
|
||||
- add missing exports in CJS typings
|
||||
|
||||
|
||||
**5.3.4 / 2026-01-30**
|
||||
- fix: handle HTML numeric and hex entities when out of range
|
||||
|
||||
|
||||
**5.3.3 / 2025-12-12**
|
||||
- fix #775: transformTagName with allowBooleanAttributes adds an unnecessary attribute
|
||||
|
||||
**5.3.2 / 2025-11-14**
|
||||
- fix for import statement for v6
|
||||
|
||||
**5.3.1 / 2025-11-03**
|
||||
- Performance improvement for stopNodes (By [Maciek Lamberski](https://github.com/macieklamberski))
|
||||
|
||||
**5.3.0 / 2025-10-03**
|
||||
- Use `Uint8Array` in place of `Buffer` in Parser
|
||||
|
||||
**5.2.5 / 2025-06-08**
|
||||
- Inform user to use [fxp-cli](https://github.com/NaturalIntelligence/fxp-cli) instead of in-built CLI feature
|
||||
- Export typings for direct use
|
||||
|
||||
**5.2.4 / 2025-06-06**
|
||||
- fix (#747): fix EMPTY and ANY with ELEMENT in DOCTYPE
|
||||
|
||||
**5.2.3 / 2025-05-11**
|
||||
- fix (#747): support EMPTY and ANY with ELEMENT in DOCTYPE
|
||||
|
||||
**5.2.2 / 2025-05-05**
|
||||
- fix (#746): update strnum to fix parsing issues related to enotations
|
||||
|
||||
**5.2.1 / 2025-04-22**
|
||||
- fix: read DOCTYPE entity value correctly
|
||||
- read DOCTYPE NOTATION, ELEMENT exp but not using read values
|
||||
|
||||
|
||||
**5.2.0 / 2025-04-03**
|
||||
- feat: support metadata on nodes (#593) (By [Steven R. Loomis](https://github.com/srl295))
|
||||
|
||||
**5.1.0 / 2025-04-02**
|
||||
- feat: declare package as side-effect free (#738) (By [Thomas Bouffard](https://github.com/tbouffard))
|
||||
- fix cjs build mode
|
||||
- fix builder return type to string
|
||||
-
|
||||
|
||||
**5.0.9 / 2025-03-14**
|
||||
- fix: support numeric entities with values over 0xFFFF (#726) (By [Marc Durdin](https://github.com/mcdurdin))
|
||||
- fix: update strnum to fix parsing 0 if skiplike option is used
|
||||
|
||||
**5.0.8 / 2025-02-27**
|
||||
- fix parsing 0 if skiplike option is used.
|
||||
- updating strnum dependency
|
||||
|
||||
**5.0.7 / 2025-02-25**
|
||||
- fix (#724) typings for cjs.
|
||||
|
||||
**5.0.6 / 2025-02-20**
|
||||
- fix cli output (By [Angel Delgado](https://github.com/angeld7))
|
||||
- remove multiple JSON parsing
|
||||
|
||||
**5.0.5 / 2025-02-20**
|
||||
- fix parsing of string starting with 'e' or 'E' by updating strnum
|
||||
|
||||
**5.0.4 / 2025-02-20**
|
||||
- fix CLI to support all the versions of node js when displaying library version.
|
||||
- fix CJS import in v5
|
||||
- by fixing webpack config
|
||||
|
||||
**5.0.3 / 2025-02-20**
|
||||
- Using strnum ESM module
|
||||
- new fixes in strum may break your experience
|
||||
|
||||
**5.0.2 / 2025-02-20**
|
||||
- fix: include CommonJS resources in the npm package #714 (By [Thomas Bouffard](https://github.com/tbouffard))
|
||||
- fix: move babel deps to dev deps
|
||||
|
||||
**5.0.1 / 2025-02-19**
|
||||
- fix syntax error for CLI command
|
||||
|
||||
**5.0.0 / 2025-02-19**
|
||||
- ESM support
|
||||
- no change in the functionality, syntax, APIs, options, or documentation.
|
||||
|
||||
**4.5.2 / 2025-02-18**
|
||||
- Fix null CDATA to comply with undefined behavior (#701) (By [Matthieu BOHEAS](https://github.com/Kelgors))
|
||||
- Fix(performance): Update check for leaf node in saveTextToParentTag function in OrderedObjParser.js (#707) (By [...](https://github.com/tomingtoming))
|
||||
- Fix: emit full JSON string from CLI when no output filename specified (#710) (By [Matt Benson](https://github.com/mbenson))
|
||||
|
||||
**4.5.1 / 2024-12-15**
|
||||
- Fix empty tag key name for v5 (#697). no impact on v4
|
||||
- Fixes entity parsing when used in strict mode (#699)
|
||||
|
||||
**4.5.0 / 2024-09-03**
|
||||
- feat #666: ignoreAttributes support function, and array of string or regex (By [ArtemM](https://github.com/mav-rik))
|
||||
|
||||
**4.4.1 / 2024-07-28**
|
||||
- v5 fix: maximum length limit to currency value
|
||||
- fix #634: build attributes with oneListGroup and attributesGroupName (#653)(By [Andreas Naziris](https://github.com/a-rasin))
|
||||
- fix: get oneListGroup to work as expected for array of strings (#662)(By [Andreas Naziris](https://github.com/a-rasin))
|
||||
|
||||
**4.4.0 / 2024-05-18**
|
||||
- fix #654: parse attribute list correctly for self closing stop node.
|
||||
- fix: validator bug when closing tag is not opened. (#647) (By [Ryosuke Fukatani](https://github.com/RyosukeFukatani))
|
||||
- fix #581: typings; return type of `tagValueProcessor` & `attributeValueProcessor` (#582) (By [monholm]())
|
||||
|
||||
**4.3.6 / 2024-03-16**
|
||||
- Add support for parsing HTML numeric entities (#645) (By [Jonas Schade ](https://github.com/DerZade))
|
||||
|
||||
**4.3.5 / 2024-02-24**
|
||||
- code for v5 is added for experimental use
|
||||
|
||||
**4.3.4 / 2024-01-10**
|
||||
- fix: Don't escape entities in CDATA sections (#633) (By [wackbyte](https://github.com/wackbyte))
|
||||
|
||||
**4.3.3 / 2024-01-10**
|
||||
- Remove unnecessary regex
|
||||
|
||||
**4.3.2 / 2023-10-02**
|
||||
- fix `jObj.hasOwnProperty` when give input is null (By [Arda TANRIKULU](https://github.com/ardatan))
|
||||
|
||||
**4.3.1 / 2023-09-24**
|
||||
- revert back "Fix typings for builder and parser to make return type generic" to avoid failure of existing projects. Need to decide a common approach.
|
||||
|
||||
**4.3.0 / 2023-09-20**
|
||||
- Fix stopNodes to work with removeNSPrefix (#607) (#608) (By [Craig Andrews]https://github.com/candrews))
|
||||
- Fix #610 ignore properties set to Object.prototype
|
||||
- Fix typings for builder and parser to make return type generic (By [Sarah Dayan](https://github.com/sarahdayan))
|
||||
|
||||
**4.2.7 / 2023-07-30**
|
||||
- Fix: builder should set text node correctly when only textnode is present (#589) (By [qianqing](https://github.com/joneqian))
|
||||
- Fix: Fix for null and undefined attributes when building xml (#585) (#598). A null or undefined value should be ignored. (By [Eugenio Ceschia](https://github.com/cecia234))
|
||||
|
||||
**4.2.6 / 2023-07-17**
|
||||
- Fix: Remove trailing slash from jPath for self-closing tags (#595) (By [Maciej Radzikowski](https://github.com/m-radzikowski))
|
||||
|
||||
**4.2.5 / 2023-06-22**
|
||||
- change code implementation
|
||||
|
||||
**4.2.4 / 2023-06-06**
|
||||
- fix security bug
|
||||
|
||||
**4.2.3 / 2023-06-05**
|
||||
- fix security bug
|
||||
|
||||
**4.2.2 / 2023-04-18**
|
||||
- fix #562: fix unpaired tag when it comes in last of a nested tag. Also throw error when unpaired tag is used as closing tag
|
||||
|
||||
**4.2.1 / 2023-04-18**
|
||||
- fix: jpath after unpaired tags
|
||||
|
||||
**4.2.0 / 2023-04-09**
|
||||
- support `updateTag` parser property
|
||||
|
||||
**4.1.4 / 2023-04-08**
|
||||
- update typings to let user create XMLBuilder instance without options (#556) (By [Patrick](https://github.com/omggga))
|
||||
- fix: IsArray option isn't parsing tags with 0 as value correctly #490 (#557) (By [Aleksandr Murashkin](https://github.com/p-kuen))
|
||||
- feature: support `oneListGroup` to group repeated children tags udder single group
|
||||
|
||||
**4.1.3 / 2023-02-26**
|
||||
- fix #546: Support complex entity value
|
||||
|
||||
**4.1.2 / 2023-02-12**
|
||||
- Security Fix
|
||||
|
||||
**4.1.1 / 2023-02-03**
|
||||
- Fix #540: ignoreAttributes breaks unpairedTags
|
||||
- Refactor XML builder code
|
||||
|
||||
**4.1.0 / 2023-02-02**
|
||||
- Fix '<' or '>' in DTD comment throwing an error. (#533) (By [Adam Baker](https://github.com/Cwazywierdo))
|
||||
- Set "eNotation" to 'true' as default
|
||||
|
||||
**4.0.15 / 2023-01-25**
|
||||
- make "eNotation" optional
|
||||
|
||||
**4.0.14 / 2023-01-22**
|
||||
- fixed: add missed typing "eNotation" to parse values
|
||||
|
||||
**4.0.13 / 2023-01-07**
|
||||
- preserveorder formatting (By [mdeknowis](https://github.com/mdeknowis))
|
||||
- support `transformAttributeName` (By [Erik Rothoff Andersson](https://github.com/erkie))
|
||||
|
||||
**4.0.12 / 2022-11-19**
|
||||
- fix typescript
|
||||
|
||||
**4.0.11 / 2022-10-05**
|
||||
- fix #501: parse for entities only once
|
||||
|
||||
**4.0.10 / 2022-09-14**
|
||||
- fix broken links in demo site (By [Yannick Lang](https://github.com/layaxx))
|
||||
- fix #491: tagValueProcessor type definition (By [Andrea Francesco Speziale](https://github.com/andreafspeziale))
|
||||
- Add jsdocs for tagValueProcessor
|
||||
|
||||
|
||||
**4.0.9 / 2022-07-10**
|
||||
- fix #470: stop-tag can have self-closing tag with same name
|
||||
- fix #472: stopNode can have any special tag inside
|
||||
- Allow !ATTLIST and !NOTATION with DOCTYPE
|
||||
- Add transformTagName option to transform tag names when parsing (#469) (By [Erik Rothoff Andersson](https://github.com/erkie))
|
||||
|
||||
**4.0.8 / 2022-05-28**
|
||||
- Fix CDATA parsing returning empty string when value = 0 (#451) (By [ndelanou](https://github.com/ndelanou))
|
||||
- Fix stopNodes when same tag appears inside node (#456) (By [patrickshipe](https://github.com/patrickshipe))
|
||||
- fix #468: prettify own properties only
|
||||
|
||||
**4.0.7 / 2022-03-18**
|
||||
- support CDATA even if tag order is not preserved
|
||||
- support Comments even if tag order is not preserved
|
||||
- fix #446: XMLbuilder should not indent XML declaration
|
||||
|
||||
**4.0.6 / 2022-03-08**
|
||||
- fix: call tagValueProcessor only once for array items
|
||||
- fix: missing changed for #437
|
||||
|
||||
**4.0.5 / 2022-03-06**
|
||||
- fix #437: call tagValueProcessor from XML builder
|
||||
|
||||
**4.0.4 / 2022-03-03**
|
||||
- fix #435: should skip unpaired and self-closing nodes when set as stopnodes
|
||||
|
||||
**4.0.3 / 2022-02-15**
|
||||
- fix: ReferenceError when Bundled with Strict (#431) (By [Andreas Heissenberger](https://github.com/aheissenberger))
|
||||
|
||||
|
||||
**4.0.2 / 2022-02-04**
|
||||
- builder supports `suppressUnpairedNode`
|
||||
- parser supports `ignoreDeclaration` and `ignorePiTags`
|
||||
- fix: when comment is parsed as text value if given as `<!--> ...` #423
|
||||
- builder supports decoding `&`
|
||||
|
||||
**4.0.1 / 2022-01-08**
|
||||
- fix builder for pi tag
|
||||
- fix: support suppressBooleanAttrs by builder
|
||||
|
||||
**4.0.0 / 2022-01-06**
|
||||
- Generating different combined, parser only, builder only, validator only browser bundles
|
||||
- Keeping cjs modules as they can be imported in cjs and esm modules both. Otherwise refer `esm` branch.
|
||||
|
||||
**4.0.0-beta.8 / 2021-12-13**
|
||||
- call tagValueProcessor for stop nodes
|
||||
|
||||
**4.0.0-beta.7 / 2021-12-09**
|
||||
- fix Validator bug when an attribute has no value but '=' only
|
||||
- XML Builder should suppress unpaired tags by default.
|
||||
- documents update for missing features
|
||||
- refactoring to use Object.assign
|
||||
- refactoring to remove repeated code
|
||||
|
||||
**4.0.0-beta.6 / 2021-12-05**
|
||||
- Support PI Tags processing
|
||||
- Support `suppressBooleanAttributes` by XML Builder for attributes with value `true`.
|
||||
|
||||
**4.0.0-beta.5 / 2021-12-04**
|
||||
- fix: when a tag with name "attributes"
|
||||
|
||||
**4.0.0-beta.4 / 2021-12-02**
|
||||
- Support HTML document parsing
|
||||
- skip stop nodes parsing when building the XML from JS object
|
||||
- Support external entites without DOCTYPE
|
||||
- update dev dependency: strnum v1.0.5 to fix long number issue
|
||||
|
||||
**4.0.0-beta.3 / 2021-11-30**
|
||||
- support global stopNodes expression like "*.stop"
|
||||
- support self-closing and paired unpaired tags
|
||||
- fix: CDATA should not be parsed.
|
||||
- Fix typings for XMLBuilder (#396)(By [Anders Emil Salvesen](https://github.com/andersem))
|
||||
- supports XML entities, HTML entities, DOCTYPE entities
|
||||
|
||||
**⚠️ 4.0.0-beta.2 / 2021-11-19**
|
||||
- rename `attrMap` to `attibutes` in parser output when `preserveOrder:true`
|
||||
- supports unpairedTags
|
||||
|
||||
**⚠️ 4.0.0-beta.1 / 2021-11-18**
|
||||
- Parser returns an array now
|
||||
- to make the structure common
|
||||
- and to return root level detail
|
||||
- renamed `cdataTagName` to `cdataPropName`
|
||||
- Added `commentPropName`
|
||||
- fix typings
|
||||
|
||||
**⚠️ 4.0.0-beta.0 / 2021-11-16**
|
||||
- Name change of many configuration properties.
|
||||
- `attrNodeName` to `attributesGroupName`
|
||||
- `attrValueProcessor` to `attributeValueProcessor`
|
||||
- `parseNodeValue` to `parseTagValue`
|
||||
- `ignoreNameSpace` to `removeNSPrefix`
|
||||
- `numParseOptions` to `numberParseOptions`
|
||||
- spelling correction for `suppressEmptyNode`
|
||||
- Name change of cli and browser bundle to **fxparser**
|
||||
- `isArray` option is added to parse a tag into array
|
||||
- `preserveOrder` option is added to render XML in such a way that the result js Object maintains the order of properties same as in XML.
|
||||
- Processing behaviour of `tagValueProcessor` and `attributeValueProcessor` are changes with extra input parameters
|
||||
- j2xparser is renamed to XMLBuilder.
|
||||
- You need to build XML parser instance for given options first before parsing XML.
|
||||
- fix #327, #336: throw error when extra text after XML content
|
||||
- fix #330: attribute value can have '\n',
|
||||
- fix #350: attrbiutes can be separated by '\n' from tagname
|
||||
|
||||
3.21.1 / 2021-10-31
|
||||
- Correctly format JSON elements with a text prop but no attribute props ( By [haddadnj](https://github.com/haddadnj) )
|
||||
|
||||
3.21.0 / 2021-10-25
|
||||
- feat: added option `rootNodeName` to set tag name for array input when converting js object to XML.
|
||||
- feat: added option `alwaysCreateTextNode` to force text node creation (by: *@massimo-ua*)
|
||||
- ⚠️ feat: Better error location for unclosed tags. (by *@Gei0r*)
|
||||
- Some error messages would be changed when validating XML. Eg
|
||||
- `{ InvalidXml: "Invalid '[ \"rootNode\"]' found." }` → `{InvalidTag: "Unclosed tag 'rootNode'."}`
|
||||
- `{ InvalidTag: "Closing tag 'rootNode' is expected inplace of 'rootnode'." }` → `{ InvalidTag: "Expected closing tag 'rootNode' (opened in line 1) instead of closing tag 'rootnode'."}`
|
||||
- ⚠️ feat: Column in error response when validating XML
|
||||
```js
|
||||
{
|
||||
"code": "InvalidAttr",
|
||||
"msg": "Attribute 'abc' is repeated.",
|
||||
"line": 1,
|
||||
"col": 22
|
||||
}
|
||||
```
|
||||
|
||||
3.20.1 / 2021-09-25
|
||||
- update strnum package
|
||||
|
||||
3.20.0 / 2021-09-10
|
||||
- Use strnum npm package to parse string to number
|
||||
- breaking change: long number will be parsed to scientific notation.
|
||||
|
||||
3.19.0 / 2021-03-14
|
||||
- License changed to MIT original
|
||||
- Fix #321 : namespace tag parsing
|
||||
|
||||
3.18.0 / 2021-02-05
|
||||
- Support RegEx and function in arrayMode option
|
||||
- Fix #317 : validate nested PI tags
|
||||
|
||||
3.17.4 / 2020-06-07
|
||||
- Refactor some code to support IE11
|
||||
- Fix: `<tag >` space as attribute string
|
||||
|
||||
3.17.3 / 2020-05-23
|
||||
- Fix: tag name separated by \n \t
|
||||
- Fix: throw error for unclosed tags
|
||||
|
||||
3.17.2 / 2020-05-23
|
||||
- Fixed an issue in processing doctype tag
|
||||
- Fixed tagName where it should not have whitespace chars
|
||||
|
||||
3.17.1 / 2020-05-19
|
||||
- Fixed an issue in checking opening tag
|
||||
|
||||
3.17.0 / 2020-05-18
|
||||
- parser: fix '<' issue when it comes in aatr value
|
||||
- parser: refactoring to remove dependency from regex
|
||||
- validator: fix IE 11 issue for error messages
|
||||
- updated dev dependencies
|
||||
- separated benchmark module to sub-module
|
||||
- breaking change: comments will not be removed from CDATA data
|
||||
|
||||
3.16.0 / 2020-01-12
|
||||
- validaor: fix for ampersand characters (#215)
|
||||
- refactoring to support unicode chars in tag name
|
||||
- update typing for validator error
|
||||
|
||||
3.15.1 / 2019-12-09
|
||||
- validaor: fix multiple roots are not allowed
|
||||
|
||||
3.15.0 / 2019-11-23
|
||||
- validaor: improve error messaging
|
||||
- validator: add line number in case of error
|
||||
- validator: add more error scenarios to make it more descriptive
|
||||
|
||||
3.14.0 / 2019-10-25
|
||||
- arrayMode for XML to JS obj parsing
|
||||
|
||||
3.13.0 / 2019-10-02
|
||||
- pass tag/attr name to tag/attr value processor
|
||||
- inbuilt optional validation with XML parser
|
||||
|
||||
3.12.21 / 2019-10-02
|
||||
- Fix validator for unclosed XMLs
|
||||
- move nimnjs dependency to dev dependency
|
||||
- update dependencies
|
||||
|
||||
3.12.20 / 2019-08-16
|
||||
- Revert: Fix #167: '>' in attribute value as it is causing high performance degrade.
|
||||
|
||||
3.12.19 / 2019-07-28
|
||||
- Fix js to xml parser should work for date values. (broken: `tagValueProcessor` will receive the original value instead of string always) (breaking change)
|
||||
|
||||
3.12.18 / 2019-07-27
|
||||
- remove configstore dependency
|
||||
|
||||
3.12.17 / 2019-07-14
|
||||
- Fix #167: '>' in attribute value
|
||||
|
||||
3.12.16 / 2019-03-23
|
||||
- Support a new option "stopNodes". (#150)
|
||||
Accept the list of tags which are not required to be parsed. Instead, all the nested tag and data will be assigned as string.
|
||||
- Don't show post-install message
|
||||
|
||||
3.12.12 / 2019-01-11
|
||||
- fix : IE parseInt, parseFloat error
|
||||
|
||||
3.12.11 / 2018-12-24
|
||||
- fix #132: "/" should not be parsed as boolean attr in case of self closing tags
|
||||
|
||||
3.12.9 / 2018-11-23
|
||||
- fix #129 : validator should not fail when an atrribute name is 'length'
|
||||
|
||||
3.12.8 / 2018-11-22
|
||||
- fix #128 : use 'attrValueProcessor' to process attribute value in json2xml parser
|
||||
|
||||
3.12.6 / 2018-11-10
|
||||
- Fix #126: check for type
|
||||
|
||||
3.12.4 / 2018-09-12
|
||||
- Fix: include tasks in npm package
|
||||
|
||||
3.12.3 / 2018-09-12
|
||||
- Fix CLI issue raised in last PR
|
||||
|
||||
3.12.2 / 2018-09-11
|
||||
- Fix formatting for JSON to XML output
|
||||
- Migrate to webpack (PR merged)
|
||||
- fix cli (PR merged)
|
||||
|
||||
3.12.0 / 2018-08-06
|
||||
- Support hexadecimal values
|
||||
- Support true number parsing
|
||||
|
||||
3.11.2 / 2018-07-23
|
||||
- Update Demo for more options
|
||||
- Update license information
|
||||
- Update readme for formatting, users, and spelling mistakes
|
||||
- Add missing typescript definition for j2xParser
|
||||
- refactoring: change filenames
|
||||
|
||||
3.11.1 / 2018-06-05
|
||||
- fix #93: read the text after self closing tag
|
||||
|
||||
3.11.0 / 2018-05-20
|
||||
- return defaultOptions if there are not options in buildOptions function
|
||||
- added localeRange declaration in parser.d.ts
|
||||
- Added support of cyrillic characters in validator XML
|
||||
- fixed bug in validator work when XML data with byte order marker
|
||||
|
||||
3.10.0 / 2018-05-13
|
||||
- Added support of cyrillic characters in parsing XML to JSON
|
||||
|
||||
3.9.11 / 2018-05-09
|
||||
- fix https://github.com/NaturalIntelligence/fast-xml-parser/issues/80 fix nimn chars
|
||||
- update package information
|
||||
- fix https://github.com/NaturalIntelligence/fast-xml-parser/issues/86: json 2 xml parser : property with null value should be parsed to self closing tag.
|
||||
- update online demo
|
||||
- revert zombiejs to old version to support old version of node
|
||||
- update dependencies
|
||||
|
||||
3.3.10 / 2018-04-23
|
||||
- fix #77 : parse even if closing tag has space before '>'
|
||||
- include all css & js lib in demo app
|
||||
- remove babel dependencies until needed
|
||||
|
||||
3.3.9 / 2018-04-18
|
||||
- fix #74 : TS2314 TypeScript compiler error
|
||||
|
||||
3.3.8 / 2018-04-17
|
||||
- fix #73 : IE doesn't support Object.assign
|
||||
|
||||
3.3.7 / 2018-04-14
|
||||
- fix: use let insted of const in for loop of validator
|
||||
- Merge pull request
|
||||
https://github.com/NaturalIntelligence/fast-xml-parser/issues/71 from bb/master
|
||||
first draft of typings for typescript
|
||||
https://github.com/NaturalIntelligence/fast-xml-parser/issues/69
|
||||
- Merge pull request
|
||||
https://github.com/NaturalIntelligence/fast-xml-parser/issues/70 from bb/patch-1
|
||||
fix some typos in readme
|
||||
|
||||
3.3.6 / 2018-03-21
|
||||
- change arrow functions to full notation for IE compatibility
|
||||
|
||||
3.3.5 / 2018-03-15
|
||||
- fix https://github.com/NaturalIntelligence/fast-xml-parser/issues/67 : attrNodeName invalid behavior
|
||||
- fix: remove decodeHTML char condition
|
||||
|
||||
3.3.4 / 2018-03-14
|
||||
- remove dependency on "he" package
|
||||
- refactor code to separate methods in separate files.
|
||||
- draft code for transforming XML to json string. It is not officially documented due to performance issue.
|
||||
|
||||
3.3.0 / 2018-03-05
|
||||
- use common default options for XML parsing for consistency. And add `parseToNimn` method.
|
||||
- update nexttodo
|
||||
- update README about XML to Nimn transformation and remove special notes about 3.x release
|
||||
- update CONTRIBUTING.ms mentioning nexttodo
|
||||
- add negative case for XML PIs
|
||||
- validate xml processing instruction tags https://github.com/NaturalIntelligence/fast-xml-parser/issues/62
|
||||
- nimndata: handle array with object
|
||||
- nimndata: node with nested node and text node
|
||||
- nimndata: handle attributes and text node
|
||||
- nimndata: add options, handle array
|
||||
- add xml to nimn data converter
|
||||
- x2j: direct access property with tagname
|
||||
- update changelog
|
||||
- fix validator when single quote presents in value enclosed with double quotes or vice versa
|
||||
- Revert "remove unneded nimnjs dependency, move opencollective to devDependencies and replace it
|
||||
with more light opencollective-postinstall"
|
||||
This reverts commit d47aa7181075d82db4fee97fd8ea32b056fe3f46.
|
||||
- Merge pull request: https://github.com/NaturalIntelligence/fast-xml-parser/issues/63 from HaroldPutman/suppress-undefined
|
||||
Keep undefined nodes out of the XML output : This is useful when you are deleting nodes from the JSON and rewriting XML.
|
||||
|
||||
3.2.4 / 2018-03-01
|
||||
- fix #59 fix in validator when open quote presents in attribute value
|
||||
- Create nexttodo.md
|
||||
- exclude static from bitHound tests
|
||||
- add package lock
|
||||
|
||||
3.2.3 / 2018-02-28
|
||||
- Merge pull request from Delagen/master: fix namespaces can contain the same characters as xml names
|
||||
|
||||
3.2.2 / 2018-02-22
|
||||
- fix: attribute xmlns should not be removed if ignoreNameSpace is false
|
||||
- create CONTRIBUTING.md
|
||||
|
||||
3.2.1 / 2018-02-17
|
||||
- fix: empty attribute should be parsed
|
||||
|
||||
3.2.0 / 2018-02-16
|
||||
- Merge pull request : Dev to Master
|
||||
- Update README and version
|
||||
- j2x:add performance test
|
||||
- j2x: Remove extra empty line before closing tag
|
||||
- j2x: suppress empty nodes to self closing node if configured
|
||||
- j2x: provide option to give indentation depth
|
||||
- j2x: make optional formatting
|
||||
- j2x: encodeHTMLchat
|
||||
- j2x: handle cdata tag
|
||||
- j2x: handle grouped attributes
|
||||
- convert json to xml
|
||||
- nested object
|
||||
- array
|
||||
- attributes
|
||||
- text value
|
||||
- small refactoring
|
||||
- Merge pull request: Update cli.js to let user validate XML file or data
|
||||
- Add option for rendering CDATA as separate property
|
||||
|
||||
3.0.1 / 2018-02-09
|
||||
- fix CRLF: replace it with single space in attributes value only.
|
||||
|
||||
3.0.0 / 2018-02-08
|
||||
- change online tool with new changes
|
||||
- update info about new options
|
||||
- separate tag value processing to separate function
|
||||
- make HTML decoding optional
|
||||
- give an option to allow boolean attributes
|
||||
- change cli options as per v3
|
||||
- Correct comparison table format on README
|
||||
- update v3 information
|
||||
- some performance improvement changes
|
||||
- Make regex object local to the method and move some common methods to util
|
||||
- Change parser to
|
||||
- handle multiple instances of CDATA
|
||||
- make triming of value optionals
|
||||
- HTML decode attribute and text value
|
||||
- refactor code to separate files
|
||||
- Ignore newline chars without RE (in validator)
|
||||
- validate for XML prolog
|
||||
- Validate DOCTYPE without RE
|
||||
- Update validator to return error response
|
||||
- Update README to add detail about V3
|
||||
- Separate xmlNode model class
|
||||
- include vscode debug config
|
||||
- fix for repeated object
|
||||
- fix attribute regex for boolean attributes
|
||||
- Fix validator for invalid attributes
|
||||
2.9.4 / 2018-02-02
|
||||
- Merge pull request: Decode HTML characters
|
||||
- refactor source folder name
|
||||
- ignore bundle / browser js to be published to npm
|
||||
2.9.3 / 2018-01-26
|
||||
- Merge pull request: Correctly remove CRLF line breaks
|
||||
- Enable to parse attribute in online editor
|
||||
- Fix testing demo app test
|
||||
- Describe parsing options
|
||||
- Add options for online demo
|
||||
2.9.2 / 2018-01-18
|
||||
- Remove check if tag starting with "XML"
|
||||
- Fix: when there are spaces before / after CDATA
|
||||
|
||||
2.9.1 / 2018-01-16
|
||||
- Fix: newline should be replaced with single space
|
||||
- Fix: for single and multiline comments
|
||||
- validate xml with CDATA
|
||||
- Fix: the issue when there is no space between 2 attributes
|
||||
- Fix: https://github.com/NaturalIntelligence/fast-xml-parser/issues/33: when there is newline char in attr val, it doesn't parse
|
||||
- Merge pull request: fix ignoreNamespace
|
||||
- fix: don't wrap attributes if only namespace attrs
|
||||
- fix: use portfinder for run tests, update deps
|
||||
- fix: don't treat namespaces as attributes when ignoreNamespace enabled
|
||||
|
||||
2.9.0 / 2018-01-10
|
||||
- Rewrite the validator to handle large files.
|
||||
Ignore DOCTYPE validation.
|
||||
- Fix: When attribute value has equal sign
|
||||
|
||||
2.8.3 / 2017-12-15
|
||||
- Fix: when a tag has value along with subtags
|
||||
|
||||
2.8.2 / 2017-12-04
|
||||
- Fix value parsing for IE
|
||||
|
||||
2.8.1 / 2017-12-01
|
||||
- fix: validator should return false instead of err when invalid XML
|
||||
|
||||
2.8.0 / 2017-11-29
|
||||
- Add CLI option to ignore value conversion
|
||||
- Fix variable name when filename is given on CLI
|
||||
- Update CLI help text
|
||||
- Merge pull request: xml2js: Accept standard input
|
||||
- Test Node 8
|
||||
- Update dependencies
|
||||
- Bundle readToEnd
|
||||
- Add ability to read from standard input
|
||||
|
||||
2.7.4 / 2017-09-22
|
||||
- Merge pull request: Allow wrap attributes with subobject to compatible with other parsers output
|
||||
|
||||
2.7.3 / 2017-08-02
|
||||
- fix: handle CDATA with regx
|
||||
|
||||
2.7.2 / 2017-07-30
|
||||
- Change travis config for yarn caching
|
||||
- fix validator: when tag property is same as array property
|
||||
- Merge pull request: Failing test case in validator for valid SVG
|
||||
|
||||
2.7.1 / 2017-07-26
|
||||
- Fix: Handle val 0
|
||||
|
||||
2.7.0 / 2017-07-25
|
||||
- Fix test for arrayMode
|
||||
- Merge pull request: Add arrayMode option to parse any nodes as arrays
|
||||
|
||||
2.6.0 / 2017-07-14
|
||||
- code improvement
|
||||
- Add unit tests for value conversion for attr
|
||||
- Merge pull request: option of an attribute value conversion to a number (textAttrConversion) the same way as the textNodeConversion option does. Default value is false.
|
||||
|
||||
2.5.1 / 2017-07-01
|
||||
- Fix XML element name pattern
|
||||
- Fix XML element name pattern while parsing
|
||||
- Fix validation for xml tag element
|
||||
|
||||
2.5.0 / 2017-06-25
|
||||
- Improve Validator performance
|
||||
- update attr matching regex
|
||||
- Add perf tests
|
||||
- Improve atrr regex to handle all cases
|
||||
|
||||
2.4.4 / 2017-06-08
|
||||
- Bug fix: when an attribute has single or double quote in value
|
||||
|
||||
2.4.3 / 2017-06-05
|
||||
- Bug fix: when multiple CDATA tags are given
|
||||
- Merge pull request: add option "textNodeConversion"
|
||||
- add option "textNodeConversion"
|
||||
|
||||
2.4.1 / 2017-04-14
|
||||
- fix tests
|
||||
- Bug fix: preserve initial space of node value
|
||||
- Handle CDATA
|
||||
|
||||
2.3.1 / 2017-03-15
|
||||
- Bug fix: when single self closing tag
|
||||
- Merge pull request: fix .codeclimate.yml
|
||||
- Update .codeclimate.yml - Fixed config so it does not error anymore.
|
||||
- Update .codeclimate.yml
|
||||
|
||||
2.3.0 / 2017-02-26
|
||||
- Code improvement
|
||||
- add bithound config
|
||||
- Update usage
|
||||
- Update travis to generate bundle js before running tests
|
||||
- 1.Browserify, 2. add more tests for validator
|
||||
- Add validator
|
||||
- Fix CLI default parameter bug
|
||||
|
||||
2.2.1 / 2017-02-05
|
||||
- Bug fix: CLI default option
|
||||
21
node_modules/fast-xml-parser/LICENSE
generated
vendored
Normal file
21
node_modules/fast-xml-parser/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Amit Kumar Gupta
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
225
node_modules/fast-xml-parser/README.md
generated
vendored
Normal file
225
node_modules/fast-xml-parser/README.md
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
# [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser)
|
||||
|
||||
[](https://npm.im/fast-xml-parser)
|
||||
|
||||
Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++ based libraries and no callback.
|
||||
|
||||
<img align="right" src="static/img/fxp_logo.png" width="180px" alt="FXP logo"/>
|
||||
|
||||
* Validate XML data syntactically. Use [detailed-xml-validator](https://github.com/NaturalIntelligence/detailed-xml-validator/) to verify business rules.
|
||||
* Parse XML to JS Objects and vice versa
|
||||
* Common JS, ESM, and browser compatible
|
||||
* Faster than any other pure JS implementation.
|
||||
|
||||
It can handle big files (tested up to 100mb). XML Entities, HTML entities, and DOCTYPE entites are supported. Unpaired tags (Eg `<br>` in HTML), stop nodes (Eg `<script>` in HTML) are supported. It can also preserve Order of tags in JS object
|
||||
|
||||
---
|
||||
# Your Support, Our Motivation
|
||||
|
||||
## Try out our New Thoughts
|
||||
|
||||
- WishIn - You need it if negative thoughts take over all the time <br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.solothought.wishin"> <img src="https://solothought.com/products/assets/images/wishin/YouTubeThumbnail.png" width="500px"/> </a>
|
||||
- **Flowgger**: 90% less logs size and 90% less debugging time<br>
|
||||
<a href="https://github.com/solothought/flowgger"> <img src="static/img/flowgger_h.webp" alt="Flowgger Logging Framework" width="300px"/></a>
|
||||
- [Text2Chart](https://solothought.com/text2chart/flow): interactive flow chart out of simple text.
|
||||
|
||||
## Financial Support
|
||||
|
||||
Sponsor this project
|
||||
|
||||
<a href="https://github.com/sponsors/NaturalIntelligence">
|
||||
<img src="https://raw.githubusercontent.com/NaturalIntelligence/ThankYouBackers/main/github_sponsor.png" width="180" />
|
||||
</a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/donate" target="_blank">
|
||||
<img src="https://opencollective.com/fast-xml-parser/donate/button@2x.png?color=blue" width=180 />
|
||||
</a>
|
||||
<a href="https://paypal.me/naturalintelligence"> <img src="static/img/support_paypal.svg" alt="donate button" width="180"/></a>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<!--
|
||||
### Current Sponsors
|
||||
|
||||
Check the complete list at [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers) for our sponsors and supporters.
|
||||
|
||||
Through Github
|
||||
|
||||
<a href="https://github.com/skunkteam" target="_blank"><img src="https://avatars.githubusercontent.com/u/46373671?s=60" width="60px"></a>
|
||||
<a href="https://github.com/getsentry" target="_blank"><img src="https://avatars.githubusercontent.com/u/1396951?s=60" width="60px"></a>
|
||||
|
||||
Through OpenCollective
|
||||
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/0/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/1/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/2/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/3/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/4/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/5/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/6/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/7/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/8/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/fast-xml-parser/sponsor/9/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/9/avatar.svg"></a>
|
||||
-->
|
||||
|
||||

|
||||
|
||||
> This is a donation. No goods or services are expected in return. Any requests for refunds for those purposes will be rejected.
|
||||
|
||||
## Users
|
||||
|
||||
<a href="https://github.com/renovatebot/renovate" title="renovate" ><img src="https://avatars1.githubusercontent.com/u/38656520" width="60px" ></a>
|
||||
<a href="https://vmware.com/" title="vmware" > <img src="https://avatars0.githubusercontent.com/u/473334" width="60px" ></a>
|
||||
<a href="https://opensource.microsoft.com/" title="microsoft" > <img src="https://avatars0.githubusercontent.com/u/6154722" width="60px" ></a>
|
||||
<a href="http://ibm.github.io/" title="IBM" > <img src="https://avatars2.githubusercontent.com/u/1459110" width="60px" ></a>
|
||||
<a href="http://www.smartbear.com" title="SmartBear Software" > <img src="https://avatars2.githubusercontent.com/u/1644671" width="60px" ></a>
|
||||
<a href="http://nasa.github.io/" title="NASA" > <img src="https://avatars0.githubusercontent.com/u/848102" width="60px" ></a>
|
||||
<a href="https://github.com/prettier" title="Prettier" > <img src="https://avatars0.githubusercontent.com/u/25822731" width="60px" ></a>
|
||||
<a href="http://brain.js.org/" title="brain.js" > <img src="https://avatars2.githubusercontent.com/u/23732838" width="60px" ></a>
|
||||
<a href="https://github.com/aws" title="AWS SDK" > <img src="https://avatars.githubusercontent.com/u/2232217" width="60px" ></a>
|
||||
<a href="http://www.fda.gov/" title="Food and Drug Administration " > <img src="https://avatars2.githubusercontent.com/u/6471964" width="60px" ></a>
|
||||
<a href="http://www.magento.com/" title="Magento" > <img src="https://avatars2.githubusercontent.com/u/168457" width="60px" ></a>
|
||||
<a href="https://github.com/SAP" title="SAP" > <img src="https://user-images.githubusercontent.com/7692328/204835214-d9d25b58-e3df-408d-87a3-c7d36b578ee4.png" width="60px" ></a>
|
||||
<a href="https://github.com/postmanlabs" title="postman" > <img src="https://user-images.githubusercontent.com/7692328/204835529-e9e290ad-696a-49ad-9d34-08e955704715.png" width="60px" ></a>
|
||||
<a href="https://github.com/react-native-community" title="React Native Community" > <img src="https://avatars.githubusercontent.com/u/20269980?v=4" width="60px" ></a>
|
||||
<a href="https://github.com/googleapis" title="Google APIs" > <img src="https://avatars.githubusercontent.com/u/16785467?v=4" width="60px" ></a>
|
||||
<a href="https://github.com/langchain-ai" title="Langchain AI" > <img src="https://avatars.githubusercontent.com/u/126733545?v=4" width="60px" ></a>
|
||||
<a href="https://github.com/withastro" title="Astro websie builder" > <img src="https://avatars.githubusercontent.com/u/44914786?v=4" width="60px" ></a>
|
||||
<a href="https://github.com/baidu" title="Baidu" > <img src="https://avatars.githubusercontent.com/u/13245940?v=4" width="60px" ></a>
|
||||
[more](./USERs.md)
|
||||
|
||||
<small>The list of users are mostly published by Github or communicated directly. Feel free to contact if you find any information wrong.</small>
|
||||
|
||||
---
|
||||
|
||||
# More about this library
|
||||
|
||||
## How to use
|
||||
|
||||
To use as package dependency
|
||||
`$ npm install fast-xml-parser`
|
||||
or
|
||||
`$ yarn add fast-xml-parser`
|
||||
|
||||
To use as system command
|
||||
`$ npm install fast-xml-parser -g`
|
||||
|
||||
To use it on a **webpage** include it from a [CDN](https://cdnjs.com/libraries/fast-xml-parser)
|
||||
|
||||
**Example**
|
||||
|
||||
As CLI command
|
||||
```bash
|
||||
$ fxparser some.xml
|
||||
```
|
||||
|
||||
In a node js project
|
||||
```js
|
||||
const { XMLParser, XMLBuilder, XMLValidator} = require("fast-xml-parser");
|
||||
|
||||
const parser = new XMLParser();
|
||||
let jObj = parser.parse(XMLdata);
|
||||
|
||||
const builder = new XMLBuilder();
|
||||
const xmlContent = builder.build(jObj);
|
||||
```
|
||||
|
||||
In a HTML page
|
||||
```html
|
||||
<script src="path/to/fxp.min.js"></script>
|
||||
:
|
||||
<script>
|
||||
const parser = new fxparser.XMLParser();
|
||||
parser.parse(xmlContent);
|
||||
</script>
|
||||
```
|
||||
|
||||
Bundle size
|
||||
|
||||
| Bundle Name | Size |
|
||||
| ------------------ | ---- |
|
||||
| fxbuilder.min.js | 6.5K |
|
||||
| fxparser.min.js | 20K |
|
||||
| fxp.min.js | 26K |
|
||||
| fxvalidator.min.js | 5.7K |
|
||||
|
||||
## Documents
|
||||
<table>
|
||||
<tr><td>v3</td><td>v4 and v5</td><td>v6</td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="./docs/v3/docs.md">documents</a>
|
||||
</td>
|
||||
<td><ol>
|
||||
<li><a href="./docs/v4,v5/1.GettingStarted.md">Getting Started</a></li>
|
||||
<li><a href="./docs/v4,v5/2.XMLparseOptions.md">XML Parser</a></li>
|
||||
<li><a href="./docs/v4,v5/3.XMLBuilder.md">XML Builder</a></li>
|
||||
<li><a href="./docs/v4,v5/4.XMLValidator.md">XML Validator</a></li>
|
||||
<li><a href="./docs/v4,v5/5.Entities.md">Entities</a></li>
|
||||
<li><a href="./docs/v4,v5/6.HTMLParsing.md">HTML Document Parsing</a></li>
|
||||
<li><a href="./docs/v4,v5/7.PITags.md">PI Tag processing</a></li>
|
||||
<li><a href="./docs/v4,v5/8.PathExpression.md">Path Expression</a></li>
|
||||
</ol></td>
|
||||
<td><ol>
|
||||
<li></li><a href="./docs/v6/1.GettingStarted.md">Getting Started</a></li>
|
||||
<li><a href="./docs/v6/2.Features.md">Features</a></li>
|
||||
<li><a href="./docs/v6/3.Options.md">Options</a></li>
|
||||
<li><a href="./docs/v6/4.OutputBuilders.md">Output Builders</a></li>
|
||||
<li><a href="./docs/v6/5.ValueParsers.md">Value Parsers</a></li>
|
||||
</ol></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**note**:
|
||||
- Version 6 is released with version 4 for experimental use. Based on its demand, it'll be developed and the features can be different in final release.
|
||||
- Version 5 has the same functionalities as version 4.
|
||||
|
||||
## Performance
|
||||
<small>negative means error</small>
|
||||
|
||||
### XML Parser
|
||||
|
||||
<img align="left" src="./docs/imgs/XMLParser_v4.png" width="45%" />
|
||||
<img src="./docs/imgs/XMLParser_large_v4.png" width="47%" />
|
||||
|
||||
* Y-axis: requests per second
|
||||
* X-axis: File size
|
||||
|
||||
### XML Builder
|
||||
|
||||
<img src="./docs/imgs/XMLBuilder_v4.png" width="50%" />
|
||||
* Y-axis: requests per second
|
||||
|
||||
<!-- [](https://github.com/NaturalIntelligence/ads/) -->
|
||||
|
||||
---
|
||||
|
||||
## Usage Trend
|
||||
|
||||
[Usage Trend of fast-xml-parser](https://npm-compare.com/fast-xml-parser#timeRange=THREE_YEARS)
|
||||
|
||||
<a href="https://npm-compare.com/fast-xml-parser#timeRange=THREE_YEARS" target="_blank">
|
||||
<img src="https://npm-compare.com/img/npm-trend/THREE_YEARS/fast-xml-parser.png" width="50%" alt="NPM Usage Trend of fast-xml-parser" />
|
||||
</a>
|
||||
|
||||
# Supporters
|
||||
#### Contributors
|
||||
|
||||
This project exists thanks to [all](graphs/contributors) the people who contribute. [[Contribute](docs/CONTRIBUTING.md)].
|
||||
|
||||
<a href="graphs/contributors"><img src="https://opencollective.com/fast-xml-parser/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
#### Backers from Open collective
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/fast-xml-parser#backer)]
|
||||
|
||||
<a href="https://opencollective.com/fast-xml-parser#backers" target="_blank"><img src="https://opencollective.com/fast-xml-parser/backers.svg?width=890"></a>
|
||||
|
||||
|
||||
|
||||
# License
|
||||
* MIT License
|
||||
|
||||

|
||||
2
node_modules/fast-xml-parser/lib/fxbuilder.min.js
generated
vendored
Normal file
2
node_modules/fast-xml-parser/lib/fxbuilder.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/fast-xml-parser/lib/fxbuilder.min.js.map
generated
vendored
Normal file
1
node_modules/fast-xml-parser/lib/fxbuilder.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/fast-xml-parser/lib/fxp.cjs
generated
vendored
Normal file
1
node_modules/fast-xml-parser/lib/fxp.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
603
node_modules/fast-xml-parser/lib/fxp.d.cts
generated
vendored
Normal file
603
node_modules/fast-xml-parser/lib/fxp.d.cts
generated
vendored
Normal file
@@ -0,0 +1,603 @@
|
||||
import type pem = require('./pem');
|
||||
type Expression = pem.Expression;
|
||||
type ReadonlyMatcher = pem.ReadonlyMatcher;
|
||||
|
||||
// jPath: true → string
|
||||
// jPath: false → ReadonlyMatcher
|
||||
type JPathOrMatcher = string | ReadonlyMatcher;
|
||||
type JPathOrExpression = string | Expression;
|
||||
|
||||
type ProcessEntitiesOptions = {
|
||||
/**
|
||||
* Whether to enable entity processing
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum size in characters for a single entity definition
|
||||
*
|
||||
* Defaults to `10000`
|
||||
*/
|
||||
maxEntitySize?: number;
|
||||
|
||||
/**
|
||||
* Maximum depth for nested entity references (reserved for future use)
|
||||
*
|
||||
* Defaults to `10`
|
||||
*/
|
||||
maxExpansionDepth?: number;
|
||||
|
||||
/**
|
||||
* Maximum total number of entity expansions allowed
|
||||
*
|
||||
* Defaults to `1000`
|
||||
*/
|
||||
maxTotalExpansions?: number;
|
||||
|
||||
/**
|
||||
* Maximum total expanded content length in characters
|
||||
*
|
||||
* Defaults to `100000`
|
||||
*/
|
||||
maxExpandedLength?: number;
|
||||
|
||||
/**
|
||||
* Maximum number of entities allowed in the XML
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxEntityCount?: number;
|
||||
|
||||
/**
|
||||
* Array of tag names where entity replacement is allowed.
|
||||
* If null, entities are replaced in all tags.
|
||||
*
|
||||
* Defaults to `null`
|
||||
*/
|
||||
allowedTags?: string[] | null;
|
||||
|
||||
/**
|
||||
* Custom filter function to determine if entities should be replaced in a tag
|
||||
*
|
||||
* @param tagName - The name of the current tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @returns `true` to allow entity replacement, `false` to skip
|
||||
*
|
||||
* Defaults to `null`
|
||||
*/
|
||||
tagFilter?: ((tagName: string, jPathOrMatcher: JPathOrMatcher) => boolean) | null;
|
||||
};
|
||||
|
||||
type X2jOptions = {
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when parsing
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - parses all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPathOrMatcher: JPathOrMatcher) => boolean);
|
||||
|
||||
/**
|
||||
* Whether to remove namespace string from tag and attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
removeNSPrefix?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse tag value with `strnum` package
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
parseTagValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse attribute value with `strnum` package
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
parseAttributeValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to remove surrounding whitespace from tag or attribute value
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
trimValues?: boolean;
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param tagValue - The value of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param hasAttributes - Whether the tag has attributes
|
||||
* @param isLeafNode - Whether the tag is a leaf node
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (tagName: string, tagValue: string, jPathOrMatcher: JPathOrMatcher, hasAttributes: boolean, isLeafNode: boolean) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName - The name of the attribute
|
||||
* @param attrValue - The value of the attribute
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPathOrMatcher) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (attrName: string, attrValue: string, jPathOrMatcher: JPathOrMatcher) => unknown;
|
||||
|
||||
/**
|
||||
* Options to pass to `strnum` for parsing numbers
|
||||
*
|
||||
* Defaults to `{ hex: true, leadingZeros: true, eNotation: true }`
|
||||
*/
|
||||
numberParseOptions?: strnumOptions;
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Accepts string patterns or Expression objects from path-expression-matcher
|
||||
*
|
||||
* String patterns starting with "*." are automatically converted to ".." for backward compatibility
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: JPathOrExpression[];
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Whether to always create a text node
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
alwaysCreateTextNode?: boolean;
|
||||
|
||||
/**
|
||||
* Determine whether a tag should be parsed as an array
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param isLeafNode - Whether the tag is a leaf node
|
||||
* @param isAttribute - Whether this is an attribute
|
||||
* @returns {boolean}
|
||||
*
|
||||
* Defaults to `() => false`
|
||||
*/
|
||||
isArray?: (tagName: string, jPathOrMatcher: JPathOrMatcher, isLeafNode: boolean, isAttribute: boolean) => boolean;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* When `true` - enables entity processing with default limits
|
||||
*
|
||||
* When `false` - disables all entity processing
|
||||
*
|
||||
* When `ProcessEntitiesOptions` - enables entity processing with custom configuration
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean | ProcessEntitiesOptions;
|
||||
|
||||
/**
|
||||
* Whether to process HTML entities
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
htmlEntities?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore the declaration tag from output
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignoreDeclaration?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore Pi tags
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignorePiTags?: boolean;
|
||||
|
||||
/**
|
||||
* Transform tag names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformTagName?: ((tagName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Transform attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformAttributeName?: ((attributeName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
|
||||
* Modify `attrs` object to control attributes for the given tag.
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param attrs - The attributes object
|
||||
* @returns {string} new tag name.
|
||||
* @returns false to skip the tag
|
||||
*
|
||||
* Defaults to `(tagName, jPathOrMatcher, attrs) => tagName`
|
||||
*/
|
||||
updateTag?: (tagName: string, jPathOrMatcher: JPathOrMatcher, attrs: { [k: string]: string }) => string | boolean;
|
||||
|
||||
/**
|
||||
* If true, adds a Symbol to all object nodes, accessible by {@link XMLParser.getMetaDataSymbol} with
|
||||
* metadata about each the node in the XML file.
|
||||
*/
|
||||
captureMetaData?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum number of nested tags
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxNestedTags?: number;
|
||||
|
||||
/**
|
||||
* Whether to strictly validate tag names
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
strictReservedNames?: boolean;
|
||||
|
||||
/**
|
||||
* Controls whether callbacks receive jPath as string or Matcher instance
|
||||
*
|
||||
* When `true` - callbacks receive jPath as string (backward compatible)
|
||||
*
|
||||
* When `false` - callbacks receive Matcher instance for advanced pattern matching
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
jPath?: boolean;
|
||||
|
||||
/**
|
||||
* Function to sanitize dangerous property names
|
||||
*
|
||||
* @param name - The name of the property
|
||||
* @returns {string} The sanitized name
|
||||
*
|
||||
* Defaults to `(name) => __name`
|
||||
*/
|
||||
onDangerousProperty?: (name: string) => string;
|
||||
};
|
||||
|
||||
type strnumOptions = {
|
||||
hex: boolean;
|
||||
leadingZeros: boolean,
|
||||
skipLike?: RegExp,
|
||||
eNotation?: boolean
|
||||
}
|
||||
|
||||
type validationOptions = {
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
};
|
||||
|
||||
type XmlBuilderOptions = {
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when building
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - builds all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Whether to make output pretty instead of single line
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
format?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* If `format` is set to `true`, sets the indent string
|
||||
*
|
||||
* Defaults to ` `
|
||||
*/
|
||||
indentBy?: string;
|
||||
|
||||
/**
|
||||
* Give a name to a top-level array
|
||||
*
|
||||
* Defaults to `undefined`
|
||||
*/
|
||||
arrayNodeName?: string;
|
||||
|
||||
/**
|
||||
* Create empty tags for tags with no text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
suppressEmptyNode?: boolean;
|
||||
|
||||
/**
|
||||
* Suppress an unpaired tag
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressUnpairedNode?: boolean;
|
||||
|
||||
/**
|
||||
* Don't put a value for boolean attributes
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Accepts string patterns or Expression objects from path-expression-matcher
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: JPathOrExpression[];
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPath, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName
|
||||
* @param attrValue
|
||||
* @param jPath
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPath) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean;
|
||||
|
||||
|
||||
oneListGroup?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum number of nested tags
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxNestedTags?: number;
|
||||
};
|
||||
|
||||
type ESchema = string | object | Array<string | object>;
|
||||
|
||||
type ValidationError = {
|
||||
err: {
|
||||
code: string;
|
||||
msg: string,
|
||||
line: number,
|
||||
col: number
|
||||
};
|
||||
};
|
||||
|
||||
declare class XMLParser {
|
||||
constructor(options?: X2jOptions);
|
||||
parse(xmlData: string | Uint8Array, validationOptions?: validationOptions | boolean): any;
|
||||
/**
|
||||
* Add Entity which is not by default supported by this library
|
||||
* @param entityIdentifier {string} Eg: 'ent' for &ent;
|
||||
* @param entityValue {string} Eg: '\r'
|
||||
*/
|
||||
addEntity(entityIdentifier: string, entityValue: string): void;
|
||||
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the {@link XMLMetaData}
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when {@link X2jOptions.captureMetaData}
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol(): Symbol;
|
||||
}
|
||||
|
||||
declare class XMLValidator {
|
||||
static validate(xmlData: string, options?: validationOptions): true | ValidationError;
|
||||
}
|
||||
|
||||
declare class XMLBuilder {
|
||||
constructor(options?: XmlBuilderOptions);
|
||||
build(jObj: any): string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This object is available on nodes via the symbol {@link XMLParser.getMetaDataSymbol}
|
||||
* when {@link X2jOptions.captureMetaData} is true.
|
||||
*/
|
||||
declare interface XMLMetaData {
|
||||
/** The index, if available, of the character where the XML node began in the input stream. */
|
||||
startIndex?: number;
|
||||
}
|
||||
|
||||
declare namespace fxp {
|
||||
export {
|
||||
XMLParser,
|
||||
XMLValidator,
|
||||
XMLBuilder,
|
||||
XMLMetaData,
|
||||
XmlBuilderOptions,
|
||||
X2jOptions,
|
||||
ESchema,
|
||||
ValidationError,
|
||||
strnumOptions,
|
||||
validationOptions,
|
||||
ProcessEntitiesOptions,
|
||||
Expression,
|
||||
ReadonlyMatcher,
|
||||
JPathOrMatcher,
|
||||
JPathOrExpression,
|
||||
}
|
||||
}
|
||||
|
||||
export = fxp;
|
||||
2
node_modules/fast-xml-parser/lib/fxp.min.js
generated
vendored
Normal file
2
node_modules/fast-xml-parser/lib/fxp.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/fast-xml-parser/lib/fxp.min.js.map
generated
vendored
Normal file
1
node_modules/fast-xml-parser/lib/fxp.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/fast-xml-parser/lib/fxparser.min.js
generated
vendored
Normal file
2
node_modules/fast-xml-parser/lib/fxparser.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/fast-xml-parser/lib/fxparser.min.js.map
generated
vendored
Normal file
1
node_modules/fast-xml-parser/lib/fxparser.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/fast-xml-parser/lib/fxvalidator.min.js
generated
vendored
Normal file
2
node_modules/fast-xml-parser/lib/fxvalidator.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/fast-xml-parser/lib/fxvalidator.min.js.map
generated
vendored
Normal file
1
node_modules/fast-xml-parser/lib/fxvalidator.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
148
node_modules/fast-xml-parser/lib/pem.d.cts
generated
vendored
Normal file
148
node_modules/fast-xml-parser/lib/pem.d.cts
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Types copied from path-expression-matcher
|
||||
* @version <version>
|
||||
* @updated <date>
|
||||
*
|
||||
* Update this file when path-expression-matcher releases a new version.
|
||||
* Source: https://github.com/NaturalIntelligence/path-expression-matcher
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for creating an Expression
|
||||
*/
|
||||
interface ExpressionOptions {
|
||||
/**
|
||||
* Path separator character
|
||||
* @default '.'
|
||||
*/
|
||||
separator?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed segment from an expression pattern
|
||||
*/
|
||||
interface Segment {
|
||||
type: 'tag' | 'deep-wildcard';
|
||||
tag?: string;
|
||||
namespace?: string;
|
||||
attrName?: string;
|
||||
attrValue?: string;
|
||||
position?: 'first' | 'last' | 'odd' | 'even' | 'nth';
|
||||
positionValue?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expression - Parses and stores a tag pattern expression.
|
||||
* Patterns are parsed once and stored in an optimized structure for fast matching.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const expr = new Expression("root.users.user");
|
||||
* const expr2 = new Expression("..user[id]:first");
|
||||
* const expr3 = new Expression("root/users/user", { separator: '/' });
|
||||
* ```
|
||||
*
|
||||
* Pattern Syntax:
|
||||
* - `root.users.user` — Match exact path
|
||||
* - `..user` — Match "user" at any depth (deep wildcard)
|
||||
* - `user[id]` — Match user tag with "id" attribute
|
||||
* - `user[id=123]` — Match user tag where id="123"
|
||||
* - `user:first` — Match first occurrence of user tag
|
||||
* - `ns::user` — Match user tag with namespace "ns"
|
||||
* - `ns::user[id]:first` — Combine namespace, attribute, and position
|
||||
*/
|
||||
declare class Expression {
|
||||
readonly pattern: string;
|
||||
readonly separator: string;
|
||||
readonly segments: Segment[];
|
||||
|
||||
constructor(pattern: string, options?: ExpressionOptions);
|
||||
|
||||
get length(): number;
|
||||
hasDeepWildcard(): boolean;
|
||||
hasAttributeCondition(): boolean;
|
||||
hasPositionSelector(): boolean;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReadonlyMatcher
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A live read-only view of a Matcher instance, returned by Matcher.readOnly().
|
||||
*
|
||||
* All query and inspection methods work normally and always reflect the current
|
||||
* state of the underlying matcher. State-mutating methods (`push`, `pop`,
|
||||
* `reset`, `updateCurrent`, `restore`) are not present — calling them on the
|
||||
* underlying Proxy throws a `TypeError` at runtime.
|
||||
*
|
||||
* This is the type received by all FXP user callbacks when `jPath: false`.
|
||||
*/
|
||||
interface ReadonlyMatcher {
|
||||
readonly separator: string;
|
||||
|
||||
/** Check if current path matches an Expression. */
|
||||
matches(expression: Expression): boolean;
|
||||
|
||||
/** Get current tag name, or `undefined` if path is empty. */
|
||||
getCurrentTag(): string | undefined;
|
||||
|
||||
/** Get current namespace, or `undefined` if not present. */
|
||||
getCurrentNamespace(): string | undefined;
|
||||
|
||||
/** Get attribute value of the current node. */
|
||||
getAttrValue(attrName: string): any;
|
||||
|
||||
/** Check if the current node has a given attribute. */
|
||||
hasAttr(attrName: string): boolean;
|
||||
|
||||
/** Sibling position of the current node (child index in parent). */
|
||||
getPosition(): number;
|
||||
|
||||
/** Occurrence counter of the current tag name at this level. */
|
||||
getCounter(): number;
|
||||
|
||||
/** Number of nodes in the current path. */
|
||||
getDepth(): number;
|
||||
|
||||
/** Current path as a string (e.g. `"root.users.user"`). */
|
||||
toString(separator?: string, includeNamespace?: boolean): string;
|
||||
|
||||
/** Current path as an array of tag names. */
|
||||
toArray(): string[];
|
||||
|
||||
/**
|
||||
* Create a snapshot of the current state.
|
||||
* The snapshot can be passed to the real Matcher.restore() if needed.
|
||||
*/
|
||||
snapshot(): MatcherSnapshot;
|
||||
}
|
||||
|
||||
/** Internal node structure — exposed via snapshot only. */
|
||||
interface PathNode {
|
||||
tag: string;
|
||||
namespace?: string;
|
||||
position: number;
|
||||
counter: number;
|
||||
values?: Record<string, any>;
|
||||
}
|
||||
|
||||
/** Snapshot of matcher state returned by `snapshot()` and `readOnly().snapshot()`. */
|
||||
interface MatcherSnapshot {
|
||||
path: PathNode[];
|
||||
siblingStacks: Map<string, number>[];
|
||||
}
|
||||
|
||||
declare namespace pem {
|
||||
export {
|
||||
Expression,
|
||||
ExpressionOptions,
|
||||
Segment,
|
||||
ReadonlyMatcher,
|
||||
PathNode,
|
||||
MatcherSnapshot,
|
||||
}
|
||||
}
|
||||
|
||||
export = pem;
|
||||
94
node_modules/fast-xml-parser/package.json
generated
vendored
Normal file
94
node_modules/fast-xml-parser/package.json
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"name": "fast-xml-parser",
|
||||
"version": "5.5.8",
|
||||
"description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
|
||||
"main": "./lib/fxp.cjs",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"module": "./src/fxp.js",
|
||||
"types": "./src/fxp.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./src/fxp.d.ts",
|
||||
"default": "./src/fxp.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./lib/fxp.d.cts",
|
||||
"default": "./lib/fxp.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "c8 --reporter=lcov --reporter=text jasmine spec/*spec.js",
|
||||
"test-types": "tsc --noEmit spec/typings/typings-test.ts",
|
||||
"unit": "jasmine",
|
||||
"coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
|
||||
"perf": "node ./benchmark/perfTest3.js",
|
||||
"lint": "eslint src/**/*.js spec/**/*.js benchmark/**/*.js",
|
||||
"bundle": "webpack --config webpack.cjs.config.js",
|
||||
"prettier": "prettier --write src/**/*.js",
|
||||
"checkReadiness": "publish-please --dry-run",
|
||||
"publish-please": "publish-please",
|
||||
"prepublishOnly": "publish-please guard"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NaturalIntelligence/fast-xml-parser.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fast",
|
||||
"xml",
|
||||
"json",
|
||||
"parser",
|
||||
"xml2js",
|
||||
"x2js",
|
||||
"xml2json",
|
||||
"js",
|
||||
"validator",
|
||||
"validate",
|
||||
"transformer",
|
||||
"assert",
|
||||
"js2xml",
|
||||
"json2xml",
|
||||
"html"
|
||||
],
|
||||
"author": "Amit Gupta (https://solothought.com)",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.13.10",
|
||||
"@babel/plugin-transform-runtime": "^7.13.10",
|
||||
"@babel/preset-env": "^7.13.10",
|
||||
"@babel/register": "^7.13.8",
|
||||
"@types/node": "20",
|
||||
"babel-loader": "^8.2.2",
|
||||
"c8": "^10.1.3",
|
||||
"eslint": "^8.3.0",
|
||||
"he": "^1.2.0",
|
||||
"jasmine": "^5.6.0",
|
||||
"prettier": "^3.5.1",
|
||||
"publish-please": "^5.5.2",
|
||||
"typescript": "5",
|
||||
"webpack": "^5.64.4",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"fast-xml-builder": "^1.1.4",
|
||||
"path-expression-matcher": "^1.2.0",
|
||||
"strnum": "^2.2.0"
|
||||
}
|
||||
}
|
||||
97
node_modules/fast-xml-parser/src/cli/cli.js
generated
vendored
Normal file
97
node_modules/fast-xml-parser/src/cli/cli.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
/*eslint-disable no-console*/
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import {XMLParser, XMLValidator} from "../fxp.js";
|
||||
import ReadToEnd from './read.js';
|
||||
import cmdDetail from "./man.js"
|
||||
|
||||
console.warn("\x1b[33m%s\x1b[0m", "⚠️ Warning: The built-in CLI interface is now deprecated.");
|
||||
console.warn("Please install the dedicated CLI package instead:");
|
||||
console.warn(" npm install -g fxp-cli");
|
||||
|
||||
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
||||
console.log(cmdDetail);
|
||||
} else if (process.argv[2] === '--version') {
|
||||
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
||||
const version = JSON.parse(fs.readFileSync(packageJsonPath).toString()).version;
|
||||
console.log(version);
|
||||
} else {
|
||||
const options = {
|
||||
removeNSPrefix: true,
|
||||
ignoreAttributes: false,
|
||||
parseTagValue: true,
|
||||
parseAttributeValue: true,
|
||||
};
|
||||
let fileName = '';
|
||||
let outputFileName;
|
||||
let validate = false;
|
||||
let validateOnly = false;
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
if (process.argv[i] === '-ns') {
|
||||
options.removeNSPrefix = false;
|
||||
} else if (process.argv[i] === '-a') {
|
||||
options.ignoreAttributes = true;
|
||||
} else if (process.argv[i] === '-c') {
|
||||
options.parseTagValue = false;
|
||||
options.parseAttributeValue = false;
|
||||
} else if (process.argv[i] === '-o') {
|
||||
outputFileName = process.argv[++i];
|
||||
} else if (process.argv[i] === '-v') {
|
||||
validate = true;
|
||||
} else if (process.argv[i] === '-V') {
|
||||
validateOnly = true;
|
||||
} else {
|
||||
//filename
|
||||
fileName = process.argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
const callback = function(xmlData) {
|
||||
let output = '';
|
||||
if (validateOnly) {
|
||||
output = XMLValidator.validate(xmlData);
|
||||
process.exitCode = output === true ? 0 : 1;
|
||||
} else {
|
||||
const parser = new XMLParser(options);
|
||||
output = JSON.stringify(parser.parse(xmlData,validate), null, 4);
|
||||
}
|
||||
if (outputFileName) {
|
||||
writeToFile(outputFileName, output);
|
||||
} else {
|
||||
console.log(output);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
|
||||
if (!fileName) {
|
||||
ReadToEnd.readToEnd(process.stdin, function(err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
callback(data.toString());
|
||||
});
|
||||
} else {
|
||||
fs.readFile(fileName, function(err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
callback(data.toString());
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Seems an invalid file or stream.' + e);
|
||||
}
|
||||
}
|
||||
|
||||
function writeToFile(fileName, data) {
|
||||
fs.writeFile(fileName, data, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log('JSON output has been written to ' + fileName);
|
||||
});
|
||||
}
|
||||
17
node_modules/fast-xml-parser/src/cli/man.js
generated
vendored
Normal file
17
node_modules/fast-xml-parser/src/cli/man.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import fs from 'fs';
|
||||
import { resolve } from 'path';
|
||||
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
||||
const version = JSON.parse(fs.readFileSync(packageJsonPath).toString()).version;
|
||||
|
||||
export default `Fast XML Parser ${version}
|
||||
----------------
|
||||
$ fxparser [-ns|-a|-c|-v|-V] <filename> [-o outputfile.json]
|
||||
$ cat xmlfile.xml | fxparser [-ns|-a|-c|-v|-V] [-o outputfile.json]
|
||||
|
||||
Options
|
||||
----------------
|
||||
-ns: remove namespace from tag and atrribute name.
|
||||
-a: don't parse attributes.
|
||||
-c: parse values to premitive type.
|
||||
-v: validate before parsing.
|
||||
-V: validate only.`
|
||||
43
node_modules/fast-xml-parser/src/cli/read.js
generated
vendored
Normal file
43
node_modules/fast-xml-parser/src/cli/read.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
import { Transform } from 'stream';
|
||||
|
||||
export default class ReadToEnd extends Transform {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._encoding = options.encoding || 'utf8';
|
||||
this._buffer = '';
|
||||
}
|
||||
|
||||
_transform(chunk, encoding, done) {
|
||||
this._buffer += chunk.toString(this._encoding);
|
||||
this.push(chunk);
|
||||
done();
|
||||
}
|
||||
|
||||
_flush(done) {
|
||||
this.emit('complete', null, this._buffer);
|
||||
done();
|
||||
}
|
||||
|
||||
static readToEnd(stream, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
const dest = new ReadToEnd(options);
|
||||
|
||||
stream.pipe(dest);
|
||||
|
||||
stream.on('error', (err) => {
|
||||
stream.unpipe(dest);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
dest.on('complete', callback);
|
||||
dest.resume();
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
579
node_modules/fast-xml-parser/src/fxp.d.ts
generated
vendored
Normal file
579
node_modules/fast-xml-parser/src/fxp.d.ts
generated
vendored
Normal file
@@ -0,0 +1,579 @@
|
||||
import type { Expression, ReadonlyMatcher } from './pem';
|
||||
|
||||
// jPath: true → string
|
||||
// jPath: false → ReadonlyMatcher
|
||||
type JPathOrMatcher = string | ReadonlyMatcher;
|
||||
type JPathOrExpression = string | Expression;
|
||||
|
||||
export type ProcessEntitiesOptions = {
|
||||
/**
|
||||
* Whether to enable entity processing
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum size in characters for a single entity definition
|
||||
*
|
||||
* Defaults to `10000`
|
||||
*/
|
||||
maxEntitySize?: number;
|
||||
|
||||
/**
|
||||
* Maximum depth for nested entity references (reserved for future use)
|
||||
*
|
||||
* Defaults to `10`
|
||||
*/
|
||||
maxExpansionDepth?: number;
|
||||
|
||||
/**
|
||||
* Maximum total number of entity expansions allowed
|
||||
*
|
||||
* Defaults to `1000`
|
||||
*/
|
||||
maxTotalExpansions?: number;
|
||||
|
||||
/**
|
||||
* Maximum total expanded content length in characters
|
||||
*
|
||||
* Defaults to `100000`
|
||||
*/
|
||||
maxExpandedLength?: number;
|
||||
|
||||
/**
|
||||
* Maximum number of entities allowed in the XML
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxEntityCount?: number;
|
||||
|
||||
/**
|
||||
* Array of tag names where entity replacement is allowed.
|
||||
* If null, entities are replaced in all tags.
|
||||
*
|
||||
* Defaults to `null`
|
||||
*/
|
||||
allowedTags?: string[] | null;
|
||||
|
||||
/**
|
||||
* Custom filter function to determine if entities should be replaced in a tag
|
||||
*
|
||||
* @param tagName - The name of the current tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @returns `true` to allow entity replacement, `false` to skip
|
||||
*
|
||||
* Defaults to `null`
|
||||
*/
|
||||
tagFilter?: ((tagName: string, jPathOrMatcher: JPathOrMatcher) => boolean) | null;
|
||||
};
|
||||
|
||||
export type X2jOptions = {
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when parsing
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - parses all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPathOrMatcher: JPathOrMatcher) => boolean);
|
||||
|
||||
/**
|
||||
* Whether to remove namespace string from tag and attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
removeNSPrefix?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse tag value with `strnum` package
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
parseTagValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to parse attribute value with `strnum` package
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
parseAttributeValue?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to remove surrounding whitespace from tag or attribute value
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
trimValues?: boolean;
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param tagValue - The value of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param hasAttributes - Whether the tag has attributes
|
||||
* @param isLeafNode - Whether the tag is a leaf node
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (tagName: string, tagValue: string, jPathOrMatcher: JPathOrMatcher, hasAttributes: boolean, isLeafNode: boolean) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName - The name of the attribute
|
||||
* @param attrValue - The value of the attribute
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPathOrMatcher) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (attrName: string, attrValue: string, jPathOrMatcher: JPathOrMatcher) => unknown;
|
||||
|
||||
/**
|
||||
* Options to pass to `strnum` for parsing numbers
|
||||
*
|
||||
* Defaults to `{ hex: true, leadingZeros: true, eNotation: true }`
|
||||
*/
|
||||
numberParseOptions?: strnumOptions;
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Accepts string patterns or Expression objects from path-expression-matcher
|
||||
*
|
||||
* String patterns starting with "*." are automatically converted to ".." for backward compatibility
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: JPathOrExpression[];
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Whether to always create a text node
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
alwaysCreateTextNode?: boolean;
|
||||
|
||||
/**
|
||||
* Determine whether a tag should be parsed as an array
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param isLeafNode - Whether the tag is a leaf node
|
||||
* @param isAttribute - Whether this is an attribute
|
||||
* @returns {boolean}
|
||||
*
|
||||
* Defaults to `() => false`
|
||||
*/
|
||||
isArray?: (tagName: string, jPathOrMatcher: JPathOrMatcher, isLeafNode: boolean, isAttribute: boolean) => boolean;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* When `true` - enables entity processing with default limits
|
||||
*
|
||||
* When `false` - disables all entity processing
|
||||
*
|
||||
* When `ProcessEntitiesOptions` - enables entity processing with custom configuration
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean | ProcessEntitiesOptions;
|
||||
|
||||
/**
|
||||
* Whether to process HTML entities
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
htmlEntities?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore the declaration tag from output
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignoreDeclaration?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to ignore Pi tags
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
ignorePiTags?: boolean;
|
||||
|
||||
/**
|
||||
* Transform tag names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformTagName?: ((tagName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Transform attribute names
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
transformAttributeName?: ((attributeName: string) => string) | false;
|
||||
|
||||
/**
|
||||
* Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
|
||||
* Modify `attrs` object to control attributes for the given tag.
|
||||
*
|
||||
* @param tagName - The name of the tag
|
||||
* @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
|
||||
* @param attrs - The attributes object
|
||||
* @returns {string} new tag name.
|
||||
* @returns false to skip the tag
|
||||
*
|
||||
* Defaults to `(tagName, jPathOrMatcher, attrs) => tagName`
|
||||
*/
|
||||
updateTag?: (tagName: string, jPathOrMatcher: JPathOrMatcher, attrs: { [k: string]: string }) => string | boolean;
|
||||
|
||||
/**
|
||||
* If true, adds a Symbol to all object nodes, accessible by {@link XMLParser.getMetaDataSymbol} with
|
||||
* metadata about each the node in the XML file.
|
||||
*/
|
||||
captureMetaData?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum number of nested tags
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxNestedTags?: number;
|
||||
|
||||
/**
|
||||
* Whether to strictly validate tag names
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
strictReservedNames?: boolean;
|
||||
|
||||
/**
|
||||
* Controls whether callbacks receive jPath as string or Matcher instance
|
||||
*
|
||||
* When `true` - callbacks receive jPath as string (backward compatible)
|
||||
*
|
||||
* When `false` - callbacks receive Matcher instance for advanced pattern matching
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
jPath?: boolean;
|
||||
|
||||
/**
|
||||
* Function to sanitize dangerous property names
|
||||
*
|
||||
* @param name - The name of the property
|
||||
* @returns {string} The sanitized name
|
||||
*
|
||||
* Defaults to `(name) => __name`
|
||||
*/
|
||||
onDangerousProperty?: (name: string) => string;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export type strnumOptions = {
|
||||
hex: boolean;
|
||||
leadingZeros: boolean,
|
||||
skipLike?: RegExp,
|
||||
eNotation?: boolean
|
||||
}
|
||||
|
||||
export type validationOptions = {
|
||||
/**
|
||||
* Whether to allow attributes without value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
allowBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
};
|
||||
|
||||
export type XmlBuilderOptions = {
|
||||
/**
|
||||
* Give a prefix to the attribute name in the resulting JS object
|
||||
*
|
||||
* Defaults to '@_'
|
||||
*/
|
||||
attributeNamePrefix?: string;
|
||||
|
||||
/**
|
||||
* A name to group all attributes of a tag under, or `false` to disable
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
attributesGroupName?: false | string;
|
||||
|
||||
/**
|
||||
* The name of the next node in the resulting JS
|
||||
*
|
||||
* Defaults to `#text`
|
||||
*/
|
||||
textNodeName?: string;
|
||||
|
||||
/**
|
||||
* Whether to ignore attributes when building
|
||||
*
|
||||
* When `true` - ignores all the attributes
|
||||
*
|
||||
* When `false` - builds all the attributes
|
||||
*
|
||||
* When `Array<string | RegExp>` - filters out attributes that match provided patterns
|
||||
*
|
||||
* When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
|
||||
|
||||
/**
|
||||
* Give a property name to set CDATA values to instead of merging to tag's text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
cdataPropName?: false | string;
|
||||
|
||||
/**
|
||||
* If set, parse comments and set as this property
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
commentPropName?: false | string;
|
||||
|
||||
/**
|
||||
* Whether to make output pretty instead of single line
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
format?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* If `format` is set to `true`, sets the indent string
|
||||
*
|
||||
* Defaults to ` `
|
||||
*/
|
||||
indentBy?: string;
|
||||
|
||||
/**
|
||||
* Give a name to a top-level array
|
||||
*
|
||||
* Defaults to `undefined`
|
||||
*/
|
||||
arrayNodeName?: string;
|
||||
|
||||
/**
|
||||
* Create empty tags for tags with no text value
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
suppressEmptyNode?: boolean;
|
||||
|
||||
/**
|
||||
* Suppress an unpaired tag
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressUnpairedNode?: boolean;
|
||||
|
||||
/**
|
||||
* Don't put a value for boolean attributes
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
suppressBooleanAttributes?: boolean;
|
||||
|
||||
/**
|
||||
* Preserve the order of tags in resulting JS object
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
preserveOrder?: boolean;
|
||||
|
||||
/**
|
||||
* List of tags without closing tags
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
unpairedTags?: string[];
|
||||
|
||||
/**
|
||||
* Nodes to stop parsing at
|
||||
*
|
||||
* Accepts string patterns or Expression objects from path-expression-matcher
|
||||
*
|
||||
* Defaults to `[]`
|
||||
*/
|
||||
stopNodes?: JPathOrExpression[];
|
||||
|
||||
/**
|
||||
* Control how tag value should be parsed. Called only if tag value is not empty
|
||||
*
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value.
|
||||
* @returns {unknown}
|
||||
*
|
||||
* 1. Different value or value with different data type to set new value.
|
||||
* 2. Same value to set parsed value if `parseTagValue: true`.
|
||||
*
|
||||
* Defaults to `(tagName, val, jPath, hasAttributes, isLeafNode) => val`
|
||||
*/
|
||||
tagValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Control how attribute value should be parsed
|
||||
*
|
||||
* @param attrName
|
||||
* @param attrValue
|
||||
* @param jPath
|
||||
* @returns {undefined|null} `undefined` or `null` to set original value
|
||||
* @returns {unknown}
|
||||
*
|
||||
* Defaults to `(attrName, val, jPath) => val`
|
||||
*/
|
||||
attributeValueProcessor?: (name: string, value: unknown) => unknown;
|
||||
|
||||
/**
|
||||
* Whether to process default and DOCTYPE entities
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
processEntities?: boolean;
|
||||
|
||||
|
||||
oneListGroup?: boolean;
|
||||
|
||||
/**
|
||||
* Maximum number of nested tags
|
||||
*
|
||||
* Defaults to `100`
|
||||
*/
|
||||
maxNestedTags?: number;
|
||||
};
|
||||
|
||||
type ESchema = string | object | Array<string | object>;
|
||||
|
||||
export type ValidationError = {
|
||||
err: {
|
||||
code: string;
|
||||
msg: string,
|
||||
line: number,
|
||||
col: number
|
||||
};
|
||||
};
|
||||
|
||||
export class XMLParser {
|
||||
constructor(options?: X2jOptions);
|
||||
parse(xmlData: string | Uint8Array, validationOptions?: validationOptions | boolean): any;
|
||||
/**
|
||||
* Add Entity which is not by default supported by this library
|
||||
* @param entityIdentifier {string} Eg: 'ent' for &ent;
|
||||
* @param entityValue {string} Eg: '\r'
|
||||
*/
|
||||
addEntity(entityIdentifier: string, entityValue: string): void;
|
||||
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the {@link XMLMetaData}
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when {@link X2jOptions.captureMetaData}
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol(): Symbol;
|
||||
}
|
||||
|
||||
export class XMLValidator {
|
||||
static validate(xmlData: string, options?: validationOptions): true | ValidationError;
|
||||
}
|
||||
export class XMLBuilder {
|
||||
constructor(options?: XmlBuilderOptions);
|
||||
build(jObj: any): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This object is available on nodes via the symbol {@link XMLParser.getMetaDataSymbol}
|
||||
* when {@link X2jOptions.captureMetaData} is true.
|
||||
*/
|
||||
export interface XMLMetaData {
|
||||
/** The index, if available, of the character where the XML node began in the input stream. */
|
||||
startIndex?: number;
|
||||
}
|
||||
14
node_modules/fast-xml-parser/src/fxp.js
generated
vendored
Normal file
14
node_modules/fast-xml-parser/src/fxp.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
import { validate } from './validator.js';
|
||||
import XMLParser from './xmlparser/XMLParser.js';
|
||||
import XMLBuilder from './xmlbuilder/json2xml.js';
|
||||
|
||||
const XMLValidator = {
|
||||
validate: validate
|
||||
}
|
||||
export {
|
||||
XMLParser,
|
||||
XMLValidator,
|
||||
XMLBuilder
|
||||
};
|
||||
18
node_modules/fast-xml-parser/src/ignoreAttributes.js
generated
vendored
Normal file
18
node_modules/fast-xml-parser/src/ignoreAttributes.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
export default function getIgnoreAttributesFn(ignoreAttributes) {
|
||||
if (typeof ignoreAttributes === 'function') {
|
||||
return ignoreAttributes
|
||||
}
|
||||
if (Array.isArray(ignoreAttributes)) {
|
||||
return (attrName) => {
|
||||
for (const pattern of ignoreAttributes) {
|
||||
if (typeof pattern === 'string' && attrName === pattern) {
|
||||
return true
|
||||
}
|
||||
if (pattern instanceof RegExp && pattern.test(attrName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => false
|
||||
}
|
||||
135
node_modules/fast-xml-parser/src/pem.d.ts
generated
vendored
Normal file
135
node_modules/fast-xml-parser/src/pem.d.ts
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Types copied from path-expression-matcher
|
||||
* @version <version>
|
||||
* @updated <date>
|
||||
*
|
||||
* Update this file when path-expression-matcher releases a new version.
|
||||
* Source: https://github.com/NaturalIntelligence/path-expression-matcher
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for creating an Expression
|
||||
*/
|
||||
export interface ExpressionOptions {
|
||||
/**
|
||||
* Path separator character
|
||||
* @default '.'
|
||||
*/
|
||||
separator?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed segment from an expression pattern
|
||||
*/
|
||||
export interface Segment {
|
||||
type: 'tag' | 'deep-wildcard';
|
||||
tag?: string;
|
||||
namespace?: string;
|
||||
attrName?: string;
|
||||
attrValue?: string;
|
||||
position?: 'first' | 'last' | 'odd' | 'even' | 'nth';
|
||||
positionValue?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expression - Parses and stores a tag pattern expression.
|
||||
* Patterns are parsed once and stored in an optimized structure for fast matching.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const expr = new Expression("root.users.user");
|
||||
* const expr2 = new Expression("..user[id]:first");
|
||||
* const expr3 = new Expression("root/users/user", { separator: '/' });
|
||||
* ```
|
||||
*
|
||||
* Pattern Syntax:
|
||||
* - `root.users.user` — Match exact path
|
||||
* - `..user` — Match "user" at any depth (deep wildcard)
|
||||
* - `user[id]` — Match user tag with "id" attribute
|
||||
* - `user[id=123]` — Match user tag where id="123"
|
||||
* - `user:first` — Match first occurrence of user tag
|
||||
* - `ns::user` — Match user tag with namespace "ns"
|
||||
* - `ns::user[id]:first` — Combine namespace, attribute, and position
|
||||
*/
|
||||
export class Expression {
|
||||
readonly pattern: string;
|
||||
readonly separator: string;
|
||||
readonly segments: Segment[];
|
||||
|
||||
constructor(pattern: string, options?: ExpressionOptions);
|
||||
|
||||
get length(): number;
|
||||
hasDeepWildcard(): boolean;
|
||||
hasAttributeCondition(): boolean;
|
||||
hasPositionSelector(): boolean;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReadonlyMatcher
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A live read-only view of a {@link Matcher} instance, returned by {@link Matcher.readOnly}.
|
||||
*
|
||||
* All query and inspection methods work normally and always reflect the current
|
||||
* state of the underlying matcher. State-mutating methods (`push`, `pop`,
|
||||
* `reset`, `updateCurrent`, `restore`) are not present — calling them on the
|
||||
* underlying Proxy throws a `TypeError` at runtime.
|
||||
*
|
||||
* This is the type received by all FXP user callbacks when `jPath: false`.
|
||||
*/
|
||||
export interface ReadonlyMatcher {
|
||||
readonly separator: string;
|
||||
|
||||
/** Check if current path matches an Expression. */
|
||||
matches(expression: Expression): boolean;
|
||||
|
||||
/** Get current tag name, or `undefined` if path is empty. */
|
||||
getCurrentTag(): string | undefined;
|
||||
|
||||
/** Get current namespace, or `undefined` if not present. */
|
||||
getCurrentNamespace(): string | undefined;
|
||||
|
||||
/** Get attribute value of the current node. */
|
||||
getAttrValue(attrName: string): any;
|
||||
|
||||
/** Check if the current node has a given attribute. */
|
||||
hasAttr(attrName: string): boolean;
|
||||
|
||||
/** Sibling position of the current node (child index in parent). */
|
||||
getPosition(): number;
|
||||
|
||||
/** Occurrence counter of the current tag name at this level. */
|
||||
getCounter(): number;
|
||||
|
||||
/** Number of nodes in the current path. */
|
||||
getDepth(): number;
|
||||
|
||||
/** Current path as a string (e.g. `"root.users.user"`). */
|
||||
toString(separator?: string, includeNamespace?: boolean): string;
|
||||
|
||||
/** Current path as an array of tag names. */
|
||||
toArray(): string[];
|
||||
|
||||
/**
|
||||
* Create a snapshot of the current state.
|
||||
* The snapshot can be passed to the real {@link Matcher.restore} if needed.
|
||||
*/
|
||||
snapshot(): MatcherSnapshot;
|
||||
}
|
||||
|
||||
/** Internal node structure — exposed via snapshot only. */
|
||||
export interface PathNode {
|
||||
tag: string;
|
||||
namespace?: string;
|
||||
position: number;
|
||||
counter: number;
|
||||
values?: Record<string, any>;
|
||||
}
|
||||
|
||||
/** Snapshot of matcher state returned by `snapshot()` and `readOnly().snapshot()`. */
|
||||
export interface MatcherSnapshot {
|
||||
path: PathNode[];
|
||||
siblingStacks: Map<string, number>[];
|
||||
}
|
||||
61
node_modules/fast-xml-parser/src/util.js
generated
vendored
Normal file
61
node_modules/fast-xml-parser/src/util.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
|
||||
const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
|
||||
export const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';
|
||||
const regexName = new RegExp('^' + nameRegexp + '$');
|
||||
|
||||
export function getAllMatches(string, regex) {
|
||||
const matches = [];
|
||||
let match = regex.exec(string);
|
||||
while (match) {
|
||||
const allmatches = [];
|
||||
allmatches.startIndex = regex.lastIndex - match[0].length;
|
||||
const len = match.length;
|
||||
for (let index = 0; index < len; index++) {
|
||||
allmatches.push(match[index]);
|
||||
}
|
||||
matches.push(allmatches);
|
||||
match = regex.exec(string);
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
export const isName = function (string) {
|
||||
const match = regexName.exec(string);
|
||||
return !(match === null || typeof match === 'undefined');
|
||||
}
|
||||
|
||||
export function isExist(v) {
|
||||
return typeof v !== 'undefined';
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
export function getValue(v) {
|
||||
if (exports.isExist(v)) {
|
||||
return v;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dangerous property names that could lead to prototype pollution or security issues
|
||||
*/
|
||||
export const DANGEROUS_PROPERTY_NAMES = [
|
||||
// '__proto__',
|
||||
// 'constructor',
|
||||
// 'prototype',
|
||||
'hasOwnProperty',
|
||||
'toString',
|
||||
'valueOf',
|
||||
'__defineGetter__',
|
||||
'__defineSetter__',
|
||||
'__lookupGetter__',
|
||||
'__lookupSetter__'
|
||||
];
|
||||
|
||||
export const criticalProperties = ["__proto__", "constructor", "prototype"];
|
||||
16
node_modules/fast-xml-parser/src/v6/CharsSymbol.js
generated
vendored
Normal file
16
node_modules/fast-xml-parser/src/v6/CharsSymbol.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
"<" : "<", //tag start
|
||||
">" : ">", //tag end
|
||||
"/" : "/", //close tag
|
||||
"!" : "!", //comment or docttype
|
||||
"!--" : "!--", //comment
|
||||
"-->" : "-->", //comment end
|
||||
"?" : "?", //pi
|
||||
"?>" : "?>", //pi end
|
||||
"?xml" : "?xml", //pi end
|
||||
"![" : "![", //cdata
|
||||
"]]>" : "]]>", //cdata end
|
||||
"[" : "[",
|
||||
"-" : "-",
|
||||
"D" : "D",
|
||||
}
|
||||
106
node_modules/fast-xml-parser/src/v6/EntitiesParser.js
generated
vendored
Normal file
106
node_modules/fast-xml-parser/src/v6/EntitiesParser.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
const ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
||||
const htmlEntities = {
|
||||
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
||||
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
||||
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
||||
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
||||
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
||||
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
||||
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
||||
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
||||
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
||||
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
||||
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
||||
};
|
||||
export default class EntitiesParser {
|
||||
constructor(replaceHtmlEntities) {
|
||||
this.replaceHtmlEntities = replaceHtmlEntities;
|
||||
this.docTypeEntities = {};
|
||||
this.lastEntities = {
|
||||
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
||||
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
||||
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
||||
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
||||
};
|
||||
}
|
||||
|
||||
addExternalEntities(externalEntities) {
|
||||
const entKeys = Object.keys(externalEntities);
|
||||
for (let i = 0; i < entKeys.length; i++) {
|
||||
const ent = entKeys[i];
|
||||
this.addExternalEntity(ent, externalEntities[ent])
|
||||
}
|
||||
}
|
||||
addExternalEntity(key, val) {
|
||||
validateEntityName(key);
|
||||
const escaped = key.replace(/[.\-+*:]/g, '\\.');
|
||||
if (val.indexOf("&") !== -1) {
|
||||
reportWarning(`Entity ${key} is not added as '&' is found in value;`)
|
||||
return;
|
||||
} else {
|
||||
this.lastEntities[key] = {
|
||||
regex: new RegExp("&" + escaped + ";", "g"),
|
||||
val: val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addDocTypeEntities(entities) {
|
||||
const entKeys = Object.keys(entities);
|
||||
for (let i = 0; i < entKeys.length; i++) {
|
||||
const ent = entKeys[i];
|
||||
const escaped = ent.replace(/[.\-+*:]/g, '\\.');
|
||||
this.docTypeEntities[ent] = {
|
||||
regex: new RegExp("&" + escaped + ";", "g"),
|
||||
val: entities[ent]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return this.replaceEntitiesValue(val)
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Replace DOCTYPE entities
|
||||
* 2. Replace external entities
|
||||
* 3. Replace HTML entities if asked
|
||||
* @param {string} val
|
||||
*/
|
||||
replaceEntitiesValue(val) {
|
||||
if (typeof val === "string" && val.length > 0) {
|
||||
for (let entityName in this.docTypeEntities) {
|
||||
const entity = this.docTypeEntities[entityName];
|
||||
val = val.replace(entity.regx, entity.val);
|
||||
}
|
||||
for (let entityName in this.lastEntities) {
|
||||
const entity = this.lastEntities[entityName];
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
if (this.replaceHtmlEntities) {
|
||||
for (let entityName in htmlEntities) {
|
||||
const entity = htmlEntities[entityName];
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
}
|
||||
val = val.replace(ampEntity.regex, ampEntity.val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
//an entity name should not contains special characters that may be used in regex
|
||||
//Eg !?\\\/[]$%{}^&*()<>
|
||||
const specialChar = "!?\\/[]$%{}^&*()<>|+";
|
||||
|
||||
function validateEntityName(name) {
|
||||
for (let i = 0; i < specialChar.length; i++) {
|
||||
const ch = specialChar[i];
|
||||
if (name.indexOf(ch) !== -1) throw new Error(`Invalid character ${ch} in entity name`);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
61
node_modules/fast-xml-parser/src/v6/OptionsBuilder.js
generated
vendored
Normal file
61
node_modules/fast-xml-parser/src/v6/OptionsBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
import { JsObjOutputBuilder } from './OutputBuilders/JsObjBuilder.js';
|
||||
|
||||
export const defaultOptions = {
|
||||
preserveOrder: false,
|
||||
removeNSPrefix: false, // remove NS from tag name or attribute name if true
|
||||
//ignoreRootElement : false,
|
||||
stopNodes: [], //nested tags will not be parsed even for errors
|
||||
// isArray: () => false, //User will set it
|
||||
htmlEntities: false,
|
||||
// skipEmptyListItem: false
|
||||
tags: {
|
||||
unpaired: [],
|
||||
nameFor: {
|
||||
cdata: false,
|
||||
comment: false,
|
||||
text: '#text'
|
||||
},
|
||||
separateTextProperty: false,
|
||||
},
|
||||
attributes: {
|
||||
ignore: false,
|
||||
booleanType: true,
|
||||
entities: true,
|
||||
},
|
||||
|
||||
// select: ["img[src]"],
|
||||
// stop: ["anim", "[ads]"]
|
||||
only: [], // rest tags will be skipped. It will result in flat array
|
||||
hierarchy: false, //will be used when a particular tag is set to be parsed.
|
||||
skip: [], // will be skipped from parse result. on('skip') will be triggered
|
||||
|
||||
select: [], // on('select', tag => tag ) will be called if match
|
||||
stop: [], //given tagPath will not be parsed. innerXML will be set as string value
|
||||
OutputBuilder: new JsObjOutputBuilder(),
|
||||
};
|
||||
|
||||
export const buildOptions = function (options) {
|
||||
const finalOptions = { ...defaultOptions };
|
||||
copyProperties(finalOptions, options)
|
||||
return finalOptions;
|
||||
};
|
||||
|
||||
function copyProperties(target, source) {
|
||||
for (let key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
if (key === 'OutputBuilder') {
|
||||
target[key] = source[key];
|
||||
} else if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||
// Recursively copy nested properties
|
||||
if (typeof target[key] === 'undefined') {
|
||||
target[key] = {};
|
||||
}
|
||||
copyProperties(target[key], source[key]);
|
||||
} else {
|
||||
// Copy non-nested properties
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
node_modules/fast-xml-parser/src/v6/OutputBuilders/BaseOutputBuilder.js
generated
vendored
Normal file
69
node_modules/fast-xml-parser/src/v6/OutputBuilders/BaseOutputBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
export default class BaseOutputBuilder {
|
||||
constructor() {
|
||||
// this.attributes = {};
|
||||
}
|
||||
|
||||
addAttribute(name, value) {
|
||||
if (this.options.onAttribute) {
|
||||
//TODO: better to pass tag path
|
||||
const v = this.options.onAttribute(name, value, this.tagName);
|
||||
if (v) this.attributes[v.name] = v.value;
|
||||
} else {
|
||||
name = this.options.attributes.prefix + name + this.options.attributes.suffix;
|
||||
this.attributes[name] = this.parseValue(value, this.options.attributes.valueParsers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse value by chain of parsers
|
||||
* @param {string} val
|
||||
* @returns {any} parsed value if matching parser found
|
||||
*/
|
||||
parseValue = function (val, valParsers) {
|
||||
for (let i = 0; i < valParsers.length; i++) {
|
||||
let valParser = valParsers[i];
|
||||
if (typeof valParser === "string") {
|
||||
valParser = this.registeredParsers[valParser];
|
||||
}
|
||||
if (valParser) {
|
||||
val = valParser.parse(val);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* To add a nested empty tag.
|
||||
* @param {string} key
|
||||
* @param {any} val
|
||||
*/
|
||||
_addChild(key, val) { }
|
||||
|
||||
/**
|
||||
* skip the comment if property is not set
|
||||
*/
|
||||
addComment(text) {
|
||||
if (this.options.nameFor.comment)
|
||||
this._addChild(this.options.nameFor.comment, text);
|
||||
}
|
||||
|
||||
//store CDATA separately if property is set
|
||||
//otherwise add to tag's value
|
||||
addCdata(text) {
|
||||
if (this.options.nameFor.cdata) {
|
||||
this._addChild(this.options.nameFor.cdata, text);
|
||||
} else {
|
||||
this.addRawValue(text || "");
|
||||
}
|
||||
}
|
||||
|
||||
addRawValue = text => this.addValue(text);
|
||||
|
||||
addDeclaration() {
|
||||
if (!this.options.declaration) {
|
||||
} else {
|
||||
this.addPi("?xml");
|
||||
}
|
||||
this.attributes = {}
|
||||
}
|
||||
}
|
||||
103
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsArrBuilder.js
generated
vendored
Normal file
103
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsArrBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { buildOptions, registerCommonValueParsers } from './ParserOptionsBuilder.js';
|
||||
|
||||
export default class OutputBuilder {
|
||||
constructor(options) {
|
||||
this.options = buildOptions(options);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
}
|
||||
|
||||
registerValueParser(name, parserInstance) {//existing name will override the parser without warning
|
||||
this.registeredParsers[name] = parserInstance;
|
||||
}
|
||||
|
||||
getInstance(parserOptions) {
|
||||
return new JsArrBuilder(parserOptions, this.options, this.registeredParsers);
|
||||
}
|
||||
}
|
||||
|
||||
const rootName = '!js_arr';
|
||||
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
||||
|
||||
class JsArrBuilder extends BaseOutputBuilder {
|
||||
|
||||
constructor(parserOptions, options, registeredParsers) {
|
||||
super();
|
||||
this.tagsStack = [];
|
||||
this.parserOptions = parserOptions;
|
||||
this.options = options;
|
||||
this.registeredParsers = registeredParsers;
|
||||
|
||||
this.root = new Node(rootName);
|
||||
this.currentNode = this.root;
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
addTag(tag) {
|
||||
//when a new tag is added, it should be added as child of current node
|
||||
//TODO: shift this check to the parser
|
||||
if (tag.name === "__proto__") tag.name = "#__proto__";
|
||||
|
||||
this.tagsStack.push(this.currentNode);
|
||||
this.currentNode = new Node(tag.name, this.attributes);
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node should be added by checking user's preference
|
||||
* @param {Node} node
|
||||
* @returns boolean: true if the node should not be added
|
||||
*/
|
||||
closeTag() {
|
||||
const node = this.currentNode;
|
||||
this.currentNode = this.tagsStack.pop(); //set parent node in scope
|
||||
if (this.options.onClose !== undefined) {
|
||||
//TODO TagPathMatcher
|
||||
const resultTag = this.options.onClose(node,
|
||||
new TagPathMatcher(this.tagsStack, node));
|
||||
|
||||
if (resultTag) return;
|
||||
}
|
||||
this.currentNode.child.push(node); //to parent node
|
||||
}
|
||||
|
||||
//Called by parent class methods
|
||||
_addChild(key, val) {
|
||||
// if(key === "__proto__") tagName = "#__proto__";
|
||||
this.currentNode.child.push({ [key]: val });
|
||||
// this.currentNode.leafType = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add text value child node
|
||||
* @param {string} text
|
||||
*/
|
||||
addValue(text) {
|
||||
this.currentNode.child.push({ [this.options.nameFor.text]: this.parseValue(text, this.options.tags.valueParsers) });
|
||||
}
|
||||
|
||||
addPi(name) {
|
||||
//TODO: set pi flag
|
||||
if (!this.options.ignorePiTags) {
|
||||
const node = new Node(name, this.attributes);
|
||||
this.currentNode[":@"] = this.attributes;
|
||||
this.currentNode.child.push(node);
|
||||
}
|
||||
this.attributes = {};
|
||||
}
|
||||
getOutput() {
|
||||
return this.root.child[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Node {
|
||||
constructor(tagname, attributes) {
|
||||
this.tagname = tagname;
|
||||
this.child = []; //nested tags, text, cdata, comments
|
||||
if (attributes && Object.keys(attributes).length > 0)
|
||||
this[":@"] = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OutputBuilder;
|
||||
100
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsMinArrBuilder.js
generated
vendored
Normal file
100
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsMinArrBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import {buildOptions,registerCommonValueParsers} from"./ParserOptionsBuilder.js";
|
||||
|
||||
export default class OutputBuilder{
|
||||
constructor(options){
|
||||
this.options = buildOptions(options);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
}
|
||||
|
||||
registerValueParser(name,parserInstance){//existing name will override the parser without warning
|
||||
this.registeredParsers[name] = parserInstance;
|
||||
}
|
||||
|
||||
getInstance(parserOptions){
|
||||
return new JsMinArrBuilder(parserOptions, this.options, this.registeredParsers);
|
||||
}
|
||||
}
|
||||
|
||||
import BaseOutputBuilder from "./BaseOutputBuilder.js";
|
||||
const rootName = '^';
|
||||
|
||||
class JsMinArrBuilder extends BaseOutputBuilder{
|
||||
|
||||
constructor(parserOptions, options,registeredParsers) {
|
||||
super();
|
||||
this.tagsStack = [];
|
||||
this.parserOptions = parserOptions;
|
||||
this.options = options;
|
||||
this.registeredParsers = registeredParsers;
|
||||
|
||||
this.root = {[rootName]: []};
|
||||
this.currentNode = this.root;
|
||||
this.currentNodeTagName = rootName;
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
addTag(tag){
|
||||
//when a new tag is added, it should be added as child of current node
|
||||
//TODO: shift this check to the parser
|
||||
if(tag.name === "__proto__") tag.name = "#__proto__";
|
||||
|
||||
this.tagsStack.push([this.currentNodeTagName,this.currentNode]); //this.currentNode is parent node here
|
||||
this.currentNodeTagName = tag.name;
|
||||
this.currentNode = { [tag.name]:[]}
|
||||
if(Object.keys(this.attributes).length > 0){
|
||||
this.currentNode[":@"] = this.attributes;
|
||||
this.attributes = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node should be added by checking user's preference
|
||||
* @param {Node} node
|
||||
* @returns boolean: true if the node should not be added
|
||||
*/
|
||||
closeTag(){
|
||||
const node = this.currentNode;
|
||||
const nodeName = this.currentNodeTagName;
|
||||
const arr = this.tagsStack.pop(); //set parent node in scope
|
||||
this.currentNodeTagName = arr[0];
|
||||
this.currentNode = arr[1];
|
||||
|
||||
if(this.options.onClose !== undefined){
|
||||
//TODO TagPathMatcher
|
||||
const resultTag = this.options.onClose(node,
|
||||
new TagPathMatcher(this.tagsStack,node));
|
||||
|
||||
if(resultTag) return;
|
||||
}
|
||||
this.currentNode[this.currentNodeTagName].push(node); //to parent node
|
||||
}
|
||||
|
||||
//Called by parent class methods
|
||||
_addChild(key, val){
|
||||
// if(key === "__proto__") tagName = "#__proto__";
|
||||
this.currentNode.push( {[key]: val });
|
||||
// this.currentNode.leafType = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add text value child node
|
||||
* @param {string} text
|
||||
*/
|
||||
addValue(text){
|
||||
this.currentNode[this.currentNodeTagName].push( {[this.options.nameFor.text]: this.parseValue(text, this.options.tags.valueParsers) });
|
||||
}
|
||||
|
||||
addPi(name){
|
||||
if(!this.options.ignorePiTags){
|
||||
const node = { [name]:[]}
|
||||
if(this.attributes){
|
||||
node[":@"] = this.attributes;
|
||||
}
|
||||
this.currentNode.push(node);
|
||||
}
|
||||
this.attributes = {};
|
||||
}
|
||||
getOutput(){
|
||||
return this.root[rootName];
|
||||
}
|
||||
}
|
||||
154
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsObjBuilder.js
generated
vendored
Normal file
154
node_modules/fast-xml-parser/src/v6/OutputBuilders/JsObjBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
|
||||
|
||||
import { buildOptions, registerCommonValueParsers } from './ParserOptionsBuilder.js';
|
||||
|
||||
export default class OutputBuilder {
|
||||
constructor(builderOptions) {
|
||||
this.options = buildOptions(builderOptions);
|
||||
this.registeredParsers = registerCommonValueParsers(this.options);
|
||||
}
|
||||
|
||||
registerValueParser(name, parserInstance) {//existing name will override the parser without warning
|
||||
this.registeredParsers[name] = parserInstance;
|
||||
}
|
||||
|
||||
getInstance(parserOptions) {
|
||||
return new JsObjBuilder(parserOptions, this.options, this.registeredParsers);
|
||||
}
|
||||
}
|
||||
|
||||
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
||||
const rootName = '^';
|
||||
|
||||
class JsObjBuilder extends BaseOutputBuilder {
|
||||
|
||||
constructor(parserOptions, builderOptions, registeredParsers) {
|
||||
super();
|
||||
//hold the raw detail of a tag and sequence with reference to the output
|
||||
this.tagsStack = [];
|
||||
this.parserOptions = parserOptions;
|
||||
this.options = builderOptions;
|
||||
this.registeredParsers = registeredParsers;
|
||||
|
||||
this.root = {};
|
||||
this.parent = this.root;
|
||||
this.tagName = rootName;
|
||||
this.value = {};
|
||||
this.textValue = "";
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
addTag(tag) {
|
||||
|
||||
let value = "";
|
||||
if (!isEmpty(this.attributes)) {
|
||||
value = {};
|
||||
if (this.options.attributes.groupBy) {
|
||||
value[this.options.attributes.groupBy] = this.attributes;
|
||||
} else {
|
||||
value = this.attributes;
|
||||
}
|
||||
}
|
||||
|
||||
this.tagsStack.push([this.tagName, this.textValue, this.value]); //parent tag, parent text value, parent tag value (jsobj)
|
||||
this.tagName = tag.name;
|
||||
this.value = value;
|
||||
this.textValue = "";
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node should be added by checking user's preference
|
||||
* @param {Node} node
|
||||
* @returns boolean: true if the node should not be added
|
||||
*/
|
||||
closeTag() {
|
||||
const tagName = this.tagName;
|
||||
let value = this.value;
|
||||
let textValue = this.textValue;
|
||||
|
||||
//update tag text value
|
||||
if (typeof value !== "object" && !Array.isArray(value)) {
|
||||
value = this.parseValue(textValue.trim(), this.options.tags.valueParsers);
|
||||
} else if (textValue.length > 0) {
|
||||
value[this.options.nameFor.text] = this.parseValue(textValue.trim(), this.options.tags.valueParsers);
|
||||
}
|
||||
|
||||
|
||||
let resultTag = {
|
||||
tagName: tagName,
|
||||
value: value
|
||||
};
|
||||
|
||||
if (this.options.onTagClose !== undefined) {
|
||||
//TODO TagPathMatcher
|
||||
resultTag = this.options.onClose(tagName, value, this.textValue, new TagPathMatcher(this.tagsStack, node));
|
||||
|
||||
if (!resultTag) return;
|
||||
}
|
||||
|
||||
//set parent node in scope
|
||||
let arr = this.tagsStack.pop();
|
||||
let parentTag = arr[2];
|
||||
parentTag = this._addChildTo(resultTag.tagName, resultTag.value, parentTag);
|
||||
|
||||
this.tagName = arr[0];
|
||||
this.textValue = arr[1];
|
||||
this.value = parentTag;
|
||||
}
|
||||
|
||||
_addChild(key, val) {
|
||||
if (typeof this.value === "string") {
|
||||
this.value = { [this.options.nameFor.text]: this.value };
|
||||
}
|
||||
|
||||
this._addChildTo(key, val, this.value);
|
||||
// this.currentNode.leafType = false;
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
_addChildTo(key, val, node) {
|
||||
if (typeof node === 'string') node = {};
|
||||
if (!node[key]) {
|
||||
node[key] = val;
|
||||
} else { //Repeated
|
||||
if (!Array.isArray(node[key])) { //but not stored as array
|
||||
node[key] = [node[key]];
|
||||
}
|
||||
node[key].push(val);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add text value child node
|
||||
* @param {string} text
|
||||
*/
|
||||
addValue(text) {
|
||||
//TODO: use bytes join
|
||||
if (this.textValue.length > 0) this.textValue += " " + text;
|
||||
else this.textValue = text;
|
||||
}
|
||||
|
||||
addPi(name) {
|
||||
let value = "";
|
||||
if (!isEmpty(this.attributes)) {
|
||||
value = {};
|
||||
if (this.options.attributes.groupBy) {
|
||||
value[this.options.attributes.groupBy] = this.attributes;
|
||||
} else {
|
||||
value = this.attributes;
|
||||
}
|
||||
}
|
||||
this._addChild(name, value);
|
||||
|
||||
}
|
||||
getOutput() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
function isEmpty(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
94
node_modules/fast-xml-parser/src/v6/OutputBuilders/ParserOptionsBuilder.js
generated
vendored
Normal file
94
node_modules/fast-xml-parser/src/v6/OutputBuilders/ParserOptionsBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
import trimParser from "../valueParsers/trim.js";
|
||||
import booleanParser from "../valueParsers/booleanParser.js";
|
||||
import currencyParser from "../valueParsers/currency.js";
|
||||
import numberParser from "../valueParsers/number.js";
|
||||
|
||||
const defaultOptions = {
|
||||
nameFor: {
|
||||
text: "#text",
|
||||
comment: "",
|
||||
cdata: "",
|
||||
},
|
||||
// onTagClose: () => {},
|
||||
// onAttribute: () => {},
|
||||
piTag: false,
|
||||
declaration: false, //"?xml"
|
||||
tags: {
|
||||
valueParsers: [
|
||||
// "trim",
|
||||
// "boolean",
|
||||
// "number",
|
||||
// "currency",
|
||||
// "date",
|
||||
]
|
||||
},
|
||||
attributes: {
|
||||
prefix: "@_",
|
||||
suffix: "",
|
||||
groupBy: "",
|
||||
|
||||
valueParsers: [
|
||||
// "trim",
|
||||
// "boolean",
|
||||
// "number",
|
||||
// "currency",
|
||||
// "date",
|
||||
]
|
||||
},
|
||||
dataType: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
const withJoin = ["trim", "join", /*"entities",*/"number", "boolean", "currency"/*, "date"*/]
|
||||
const withoutJoin = ["trim", /*"entities",*/"number", "boolean", "currency"/*, "date"*/]
|
||||
|
||||
export function buildOptions(options) {
|
||||
//clone
|
||||
const finalOptions = { ...defaultOptions };
|
||||
|
||||
//add config missed in cloning
|
||||
finalOptions.tags.valueParsers.push(...withJoin)
|
||||
if (!this.preserveOrder)
|
||||
finalOptions.tags.valueParsers.push(...withoutJoin);
|
||||
|
||||
//add config missed in cloning
|
||||
finalOptions.attributes.valueParsers.push(...withJoin)
|
||||
|
||||
//override configuration
|
||||
copyProperties(finalOptions, options);
|
||||
return finalOptions;
|
||||
}
|
||||
|
||||
function copyProperties(target, source) {
|
||||
for (let key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||
// Recursively copy nested properties
|
||||
if (typeof target[key] === 'undefined') {
|
||||
target[key] = {};
|
||||
}
|
||||
copyProperties(target[key], source[key]);
|
||||
} else {
|
||||
// Copy non-nested properties
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function registerCommonValueParsers(options) {
|
||||
return {
|
||||
"trim": new trimParser(),
|
||||
// "join": this.entityParser.parse,
|
||||
"boolean": new booleanParser(),
|
||||
"number": new numberParser({
|
||||
hex: true,
|
||||
leadingZeros: true,
|
||||
eNotation: true
|
||||
}),
|
||||
"currency": new currencyParser(),
|
||||
// "date": this.entityParser.parse,
|
||||
}
|
||||
}
|
||||
0
node_modules/fast-xml-parser/src/v6/Report.js
generated
vendored
Normal file
0
node_modules/fast-xml-parser/src/v6/Report.js
generated
vendored
Normal file
81
node_modules/fast-xml-parser/src/v6/TagPath.js
generated
vendored
Normal file
81
node_modules/fast-xml-parser/src/v6/TagPath.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
export default class TagPath{
|
||||
constructor(pathStr){
|
||||
let text = "";
|
||||
let tName = "";
|
||||
let pos;
|
||||
let aName = "";
|
||||
let aVal = "";
|
||||
this.stack = []
|
||||
|
||||
for (let i = 0; i < pathStr.length; i++) {
|
||||
let ch = pathStr[i];
|
||||
if(ch === " ") {
|
||||
if(text.length === 0) continue;
|
||||
tName = text; text = "";
|
||||
}else if(ch === "["){
|
||||
if(tName.length === 0){
|
||||
tName = text; text = "";
|
||||
}
|
||||
i++;
|
||||
for (; i < pathStr.length; i++) {
|
||||
ch = pathStr[i];
|
||||
if(ch=== "=") continue;
|
||||
else if(ch=== "]") {aName = text.trim(); text=""; break; i--;}
|
||||
else if(ch === "'" || ch === '"'){
|
||||
let attrEnd = pathStr.indexOf(ch,i+1);
|
||||
aVal = pathStr.substring(i+1, attrEnd);
|
||||
i = attrEnd;
|
||||
}else{
|
||||
text +=ch;
|
||||
}
|
||||
}
|
||||
}else if(ch !== " " && text.length === 0 && tName.length > 0){//reading tagName
|
||||
//save previous tag
|
||||
this.stack.push(new TagPathNode(tName,pos,aName,aVal));
|
||||
text = ch; tName = ""; aName = ""; aVal = "";
|
||||
}else{
|
||||
text+=ch;
|
||||
}
|
||||
}
|
||||
|
||||
//last tag in the path
|
||||
if(tName.length >0 || text.length>0){
|
||||
this.stack.push(new TagPathNode(text||tName,pos,aName,aVal));
|
||||
}
|
||||
}
|
||||
|
||||
match(tagStack,node){
|
||||
if(this.stack[0].name !== "*"){
|
||||
if(this.stack.length !== tagStack.length +1) return false;
|
||||
|
||||
//loop through tagPath and tagStack and match
|
||||
for (let i = 0; i < this.tagStack.length; i++) {
|
||||
if(!this.stack[i].match(tagStack[i])) return false;
|
||||
}
|
||||
}
|
||||
if(!this.stack[this.stack.length - 1].match(node)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class TagPathNode{
|
||||
constructor(name,position,attrName,attrVal){
|
||||
this.name = name;
|
||||
this.position = position;
|
||||
this.attrName = attrName,
|
||||
this.attrVal = attrVal;
|
||||
}
|
||||
|
||||
match(node){
|
||||
let matching = true;
|
||||
matching = node.name === this.name;
|
||||
if(this.position) matching = node.position === this.position;
|
||||
if(this.attrName) matching = node.attrs[this.attrName !== undefined];
|
||||
if(this.attrVal) matching = node.attrs[this.attrName !== this.attrVal];
|
||||
return matching;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log((new TagPath("* b[b]")).stack);
|
||||
// console.log((new TagPath("a[a] b[b] c")).stack);
|
||||
// console.log((new TagPath(" b [ b= 'cf sdadwa' ] a ")).stack);
|
||||
13
node_modules/fast-xml-parser/src/v6/TagPathMatcher.js
generated
vendored
Normal file
13
node_modules/fast-xml-parser/src/v6/TagPathMatcher.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import {TagPath} from './TagPath.js';
|
||||
|
||||
export default class TagPathMatcher{
|
||||
constructor(stack,node){
|
||||
this.stack = stack;
|
||||
this.node= node;
|
||||
}
|
||||
|
||||
match(path){
|
||||
const tagPath = new TagPath(path);
|
||||
return tagPath.match(this.stack, this.node);
|
||||
}
|
||||
}
|
||||
83
node_modules/fast-xml-parser/src/v6/XMLParser.js
generated
vendored
Normal file
83
node_modules/fast-xml-parser/src/v6/XMLParser.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { buildOptions } from './OptionsBuilder.js';
|
||||
import Xml2JsParser from './Xml2JsParser.js';
|
||||
|
||||
export default class XMLParser {
|
||||
|
||||
constructor(options) {
|
||||
this.externalEntities = {};
|
||||
this.options = buildOptions(options);
|
||||
// console.log(this.options)
|
||||
}
|
||||
/**
|
||||
* Parse XML data string to JS object
|
||||
* @param {string|Buffer} xmlData
|
||||
* @param {boolean|Object} validationOption
|
||||
*/
|
||||
parse(xmlData) {
|
||||
if (Array.isArray(xmlData) && xmlData.byteLength !== undefined) {
|
||||
return this.parse(xmlData);
|
||||
} else if (xmlData.toString) {
|
||||
xmlData = xmlData.toString();
|
||||
} else {
|
||||
throw new Error("XML data is accepted in String or Bytes[] form.")
|
||||
}
|
||||
// if( validationOption){
|
||||
// if(validationOption === true) validationOption = {}; //validate with default options
|
||||
|
||||
// const result = validator.validate(xmlData, validationOption);
|
||||
// if (result !== true) {
|
||||
// throw Error( `${result.err.msg}:${result.err.line}:${result.err.col}` )
|
||||
// }
|
||||
// }
|
||||
const parser = new Xml2JsParser(this.options);
|
||||
parser.entityParser.addExternalEntities(this.externalEntities);
|
||||
return parser.parse(xmlData);
|
||||
}
|
||||
/**
|
||||
* Parse XML data buffer to JS object
|
||||
* @param {string|Buffer} xmlData
|
||||
* @param {boolean|Object} validationOption
|
||||
*/
|
||||
parseBytesArr(xmlData) {
|
||||
if (Array.isArray(xmlData) && xmlData.byteLength !== undefined) {
|
||||
} else {
|
||||
throw new Error("XML data is accepted in Bytes[] form.")
|
||||
}
|
||||
const parser = new Xml2JsParser(this.options);
|
||||
parser.entityParser.addExternalEntities(this.externalEntities);
|
||||
return parser.parseBytesArr(xmlData);
|
||||
}
|
||||
/**
|
||||
* Parse XML data stream to JS object
|
||||
* @param {fs.ReadableStream} xmlDataStream
|
||||
*/
|
||||
parseStream(xmlDataStream) {
|
||||
if (!isStream(xmlDataStream)) throw new Error("FXP: Invalid stream input");
|
||||
|
||||
const orderedObjParser = new Xml2JsParser(this.options);
|
||||
orderedObjParser.entityParser.addExternalEntities(this.externalEntities);
|
||||
return orderedObjParser.parseStream(xmlDataStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Entity which is not by default supported by this library
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
*/
|
||||
addEntity(key, value) {
|
||||
if (value.indexOf("&") !== -1) {
|
||||
throw new Error("Entity value can't have '&'")
|
||||
} else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
|
||||
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'")
|
||||
} else if (value === "&") {
|
||||
throw new Error("An entity with value '&' is not permitted");
|
||||
} else {
|
||||
this.externalEntities[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isStream(stream) {
|
||||
if (stream && typeof stream.read === "function" && typeof stream.on === "function" && typeof stream.readableEnded === "boolean") return true;
|
||||
return false;
|
||||
}
|
||||
235
node_modules/fast-xml-parser/src/v6/Xml2JsParser.js
generated
vendored
Normal file
235
node_modules/fast-xml-parser/src/v6/Xml2JsParser.js
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
import StringSource from './inputSource/StringSource.js';
|
||||
import BufferSource from './inputSource/BufferSource.js';
|
||||
import {readTagExp,readClosingTagName} from './XmlPartReader.js';
|
||||
import {readComment, readCdata,readDocType,readPiTag} from './XmlSpecialTagsReader.js';
|
||||
import TagPath from './TagPath.js';
|
||||
import TagPathMatcher from './TagPathMatcher.js';
|
||||
import EntitiesParser from './EntitiesParser.js';
|
||||
|
||||
//To hold the data of current tag
|
||||
//This is usually used to compare jpath expression against current tag
|
||||
class TagDetail{
|
||||
constructor(name){
|
||||
this.name = name;
|
||||
this.position = 0;
|
||||
// this.attributes = {};
|
||||
}
|
||||
}
|
||||
|
||||
export default class Xml2JsParser {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
|
||||
this.currentTagDetail = null;
|
||||
this.tagTextData = "";
|
||||
this.tagsStack = [];
|
||||
this.entityParser = new EntitiesParser(options.htmlEntities);
|
||||
this.stopNodes = [];
|
||||
for (let i = 0; i < this.options.stopNodes.length; i++) {
|
||||
this.stopNodes.push(new TagPath(this.options.stopNodes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
parse(strData) {
|
||||
this.source = new StringSource(strData);
|
||||
this.parseXml();
|
||||
return this.outputBuilder.getOutput();
|
||||
}
|
||||
parseBytesArr(data) {
|
||||
this.source = new BufferSource(data );
|
||||
this.parseXml();
|
||||
return this.outputBuilder.getOutput();
|
||||
}
|
||||
|
||||
parseXml() {
|
||||
//TODO: Separate TagValueParser as separate class. So no scope issue in node builder class
|
||||
|
||||
//OutputBuilder should be set in XML Parser
|
||||
this.outputBuilder = this.options.OutputBuilder.getInstance(this.options);
|
||||
this.root = { root: true};
|
||||
this.currentTagDetail = this.root;
|
||||
|
||||
while(this.source.canRead()){
|
||||
let ch = this.source.readCh();
|
||||
if (ch === "") break;
|
||||
|
||||
if(ch === "<"){//tagStart
|
||||
let nextChar = this.source.readChAt(0);
|
||||
if (nextChar === "" ) throw new Error("Unexpected end of source");
|
||||
|
||||
|
||||
if(nextChar === "!" || nextChar === "?"){
|
||||
this.source.updateBufferBoundary();
|
||||
//previously collected text should be added to current node
|
||||
this.addTextNode();
|
||||
|
||||
this.readSpecialTag(nextChar);// Read DOCTYPE, comment, CDATA, PI tag
|
||||
}else if(nextChar === "/"){
|
||||
this.source.updateBufferBoundary();
|
||||
this.readClosingTag();
|
||||
// console.log(this.source.buffer.length, this.source.readable);
|
||||
// console.log(this.tagsStack.length);
|
||||
}else{//opening tag
|
||||
this.readOpeningTag();
|
||||
}
|
||||
}else{
|
||||
this.tagTextData += ch;
|
||||
}
|
||||
}//End While loop
|
||||
if(this.tagsStack.length > 0 || ( this.tagTextData !== "undefined" && this.tagTextData.trimEnd().length > 0) ) throw new Error("Unexpected data in the end of document");
|
||||
}
|
||||
|
||||
/**
|
||||
* read closing paired tag. Set parent tag in scope.
|
||||
* skip a node on user's choice
|
||||
*/
|
||||
readClosingTag(){
|
||||
const tagName = this.processTagName(readClosingTagName(this.source));
|
||||
// console.log(tagName, this.tagsStack.length);
|
||||
this.validateClosingTag(tagName);
|
||||
// All the text data collected, belongs to current tag.
|
||||
if(!this.currentTagDetail.root) this.addTextNode();
|
||||
this.outputBuilder.closeTag();
|
||||
// Since the tag is closed now, parent tag comes in scope
|
||||
this.currentTagDetail = this.tagsStack.pop();
|
||||
}
|
||||
|
||||
validateClosingTag(tagName){
|
||||
// This can't be unpaired tag, or a stop tag.
|
||||
if(this.isUnpaired(tagName) || this.isStopNode(tagName)) throw new Error(`Unexpected closing tag '${tagName}'`);
|
||||
// This must match with last opening tag
|
||||
else if(tagName !== this.currentTagDetail.name)
|
||||
throw new Error(`Unexpected closing tag '${tagName}' expecting '${this.currentTagDetail.name}'`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read paired, unpaired, self-closing, stop and special tags.
|
||||
* Create a new node
|
||||
* Push paired tag in stack.
|
||||
*/
|
||||
readOpeningTag(){
|
||||
//save previously collected text data to current node
|
||||
this.addTextNode();
|
||||
|
||||
//create new tag
|
||||
let tagExp = readTagExp(this, ">" );
|
||||
|
||||
// process and skip from tagsStack For unpaired tag, self closing tag, and stop node
|
||||
const tagDetail = new TagDetail(tagExp.tagName);
|
||||
if(this.isUnpaired(tagExp.tagName)) {
|
||||
//TODO: this will lead 2 extra stack operation
|
||||
this.outputBuilder.addTag(tagDetail);
|
||||
this.outputBuilder.closeTag();
|
||||
} else if(tagExp.selfClosing){
|
||||
this.outputBuilder.addTag(tagDetail);
|
||||
this.outputBuilder.closeTag();
|
||||
} else if(this.isStopNode(this.currentTagDetail)){
|
||||
// TODO: let's user set a stop node boundary detector for complex contents like script tag
|
||||
//TODO: pass tag name only to avoid string operations
|
||||
const content = source.readUptoCloseTag(`</${tagExp.tagName}`);
|
||||
this.outputBuilder.addTag(tagDetail);
|
||||
this.outputBuilder.addValue(content);
|
||||
this.outputBuilder.closeTag();
|
||||
}else{//paired tag
|
||||
//set new nested tag in scope.
|
||||
this.tagsStack.push(this.currentTagDetail);
|
||||
this.outputBuilder.addTag(tagDetail);
|
||||
this.currentTagDetail = tagDetail;
|
||||
}
|
||||
// console.log(tagExp.tagName,this.tagsStack.length);
|
||||
// this.options.onClose()
|
||||
|
||||
}
|
||||
|
||||
readSpecialTag(startCh){
|
||||
if(startCh == "!"){
|
||||
let nextChar = this.source.readCh();
|
||||
if (nextChar === null || nextChar === undefined) throw new Error("Unexpected ending of the source");
|
||||
|
||||
if(nextChar === "-"){//comment
|
||||
readComment(this);
|
||||
}else if(nextChar === "["){//CDATA
|
||||
readCdata(this);
|
||||
}else if(nextChar === "D"){//DOCTYPE
|
||||
readDocType(this);
|
||||
}
|
||||
}else if(startCh === "?"){
|
||||
readPiTag(this);
|
||||
}else{
|
||||
throw new Error(`Invalid tag '<${startCh}' at ${this.source.line}:${this.source.col}`)
|
||||
}
|
||||
}
|
||||
addTextNode = function() {
|
||||
// if(this.currentTagDetail){
|
||||
//save text as child node
|
||||
// if(this.currentTagDetail.tagname !== '!xml')
|
||||
if (this.tagTextData !== undefined && this.tagTextData !== "") { //store previously collected data as textNode
|
||||
if(this.tagTextData.trim().length > 0){
|
||||
//TODO: shift parsing to output builder
|
||||
|
||||
this.outputBuilder.addValue(this.replaceEntities(this.tagTextData));
|
||||
}
|
||||
this.tagTextData = "";
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
processAttrName(name){
|
||||
if(name === "__proto__") name = "#__proto__";
|
||||
name = resolveNameSpace(name, this.removeNSPrefix);
|
||||
return name;
|
||||
}
|
||||
|
||||
processTagName(name){
|
||||
if(name === "__proto__") name = "#__proto__";
|
||||
name = resolveNameSpace(name, this.removeNSPrefix);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags path from tagsStack
|
||||
*/
|
||||
tagsPath(tagName){
|
||||
//TODO: return TagPath Object. User can call match method with path
|
||||
return "";
|
||||
}
|
||||
|
||||
isUnpaired(tagName){
|
||||
return this.options.tags.unpaired.indexOf(tagName) !== -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* valid expressions are
|
||||
* tag nested
|
||||
* * nested
|
||||
* tag nested[attribute]
|
||||
* tag nested[attribute=""]
|
||||
* tag nested[attribute!=""]
|
||||
* tag nested:0 //for future
|
||||
* @param {string} tagName
|
||||
* @returns
|
||||
*/
|
||||
isStopNode(node){
|
||||
for (let i = 0; i < this.stopNodes.length; i++) {
|
||||
const givenPath = this.stopNodes[i];
|
||||
if(givenPath.match(this.tagsStack, node)) return true;
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
replaceEntities(text){
|
||||
//TODO: if option is set then replace entities
|
||||
return this.entityParser.parse(text)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveNameSpace(name, removeNSPrefix) {
|
||||
if (removeNSPrefix) {
|
||||
const parts = name.split(':');
|
||||
if(parts.length === 2){
|
||||
if (parts[0] === 'xmlns') return '';
|
||||
else return parts[1];
|
||||
}else reportError(`Multiple namespaces ${name}`)
|
||||
}
|
||||
return name;
|
||||
}
|
||||
210
node_modules/fast-xml-parser/src/v6/XmlPartReader.js
generated
vendored
Normal file
210
node_modules/fast-xml-parser/src/v6/XmlPartReader.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* find paired tag for a stop node
|
||||
* @param {string} xmlDoc
|
||||
* @param {string} tagName
|
||||
* @param {number} i : start index
|
||||
*/
|
||||
export function readStopNode(xmlDoc, tagName, i){
|
||||
const startIndex = i;
|
||||
// Starting at 1 since we already have an open tag
|
||||
let openTagCount = 1;
|
||||
|
||||
for (; i < xmlDoc.length; i++) {
|
||||
if( xmlDoc[i] === "<"){
|
||||
if (xmlDoc[i+1] === "/") {//close tag
|
||||
const closeIndex = findSubStrIndex(xmlDoc, ">", i, `${tagName} is not closed`);
|
||||
let closeTagName = xmlDoc.substring(i+2,closeIndex).trim();
|
||||
if(closeTagName === tagName){
|
||||
openTagCount--;
|
||||
if (openTagCount === 0) {
|
||||
return {
|
||||
tagContent: xmlDoc.substring(startIndex, i),
|
||||
i : closeIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc[i+1] === '?') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "?>", i+1, "StopNode is not closed.")
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc.substr(i + 1, 3) === '!--') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "-->", i+3, "StopNode is not closed.")
|
||||
i=closeIndex;
|
||||
} else if(xmlDoc.substr(i + 1, 2) === '![') {
|
||||
const closeIndex = findSubStrIndex(xmlDoc, "]]>", i, "StopNode is not closed.") - 2;
|
||||
i=closeIndex;
|
||||
} else {
|
||||
const tagData = readTagExp(xmlDoc, i, '>')
|
||||
|
||||
if (tagData) {
|
||||
const openTagName = tagData && tagData.tagName;
|
||||
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
|
||||
openTagCount++;
|
||||
}
|
||||
i=tagData.closeIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//end for loop
|
||||
}
|
||||
|
||||
/**
|
||||
* Read closing tag name
|
||||
* @param {Source} source
|
||||
* @returns tag name
|
||||
*/
|
||||
export function readClosingTagName(source){
|
||||
let text = ""; //temporary data
|
||||
while(source.canRead()){
|
||||
let ch = source.readCh();
|
||||
// if (ch === null || ch === undefined) break;
|
||||
// source.updateBuffer();
|
||||
|
||||
if (ch === ">") return text.trimEnd();
|
||||
else text += ch;
|
||||
}
|
||||
throw new Error(`Unexpected end of source. Reading '${substr}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read XML tag and build attributes map
|
||||
* This function can be used to read normal tag, pi tag.
|
||||
* This function can't be used to read comment, CDATA, DOCTYPE.
|
||||
* Eg <tag attr = ' some"' attr= ">" bool>
|
||||
* @param {string} xmlDoc
|
||||
* @param {number} startIndex starting index
|
||||
* @returns tag expression includes tag name & attribute string
|
||||
*/
|
||||
export function readTagExp(parser) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let i;
|
||||
let EOE = false;
|
||||
|
||||
for (i = 0; parser.source.canRead(i); i++) {
|
||||
const char = parser.source.readChAt(i);
|
||||
|
||||
if (char === "'" && !inDoubleQuotes) {
|
||||
inSingleQuotes = !inSingleQuotes;
|
||||
} else if (char === '"' && !inSingleQuotes) {
|
||||
inDoubleQuotes = !inDoubleQuotes;
|
||||
} else if (char === '>' && !inSingleQuotes && !inDoubleQuotes) {
|
||||
// If not inside quotes, stop reading at '>'
|
||||
EOE = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if(inSingleQuotes || inDoubleQuotes){
|
||||
throw new Error("Invalid attribute expression. Quote is not properly closed");
|
||||
}else if(!EOE) throw new Error("Unexpected closing of source. Waiting for '>'");
|
||||
|
||||
|
||||
const exp = parser.source.readStr(i);
|
||||
parser.source.updateBufferBoundary(i + 1);
|
||||
return buildTagExpObj(exp, parser)
|
||||
}
|
||||
|
||||
export function readPiExp(parser) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let i;
|
||||
let EOE = false;
|
||||
|
||||
for (i = 0; parser.source.canRead(i) ; i++) {
|
||||
const currentChar = parser.source.readChAt(i);
|
||||
const nextChar = parser.source.readChAt(i+1);
|
||||
|
||||
if (currentChar === "'" && !inDoubleQuotes) {
|
||||
inSingleQuotes = !inSingleQuotes;
|
||||
} else if (currentChar === '"' && !inSingleQuotes) {
|
||||
inDoubleQuotes = !inDoubleQuotes;
|
||||
}
|
||||
|
||||
if (!inSingleQuotes && !inDoubleQuotes) {
|
||||
if (currentChar === '?' && nextChar === '>') {
|
||||
EOE = true;
|
||||
break; // Exit the loop when '?>' is found
|
||||
}
|
||||
}
|
||||
}
|
||||
if(inSingleQuotes || inDoubleQuotes){
|
||||
throw new Error("Invalid attribute expression. Quote is not properly closed in PI tag expression");
|
||||
}else if(!EOE) throw new Error("Unexpected closing of source. Waiting for '?>'");
|
||||
|
||||
if(!parser.options.attributes.ignore){
|
||||
//TODO: use regex to verify attributes if not set to ignore
|
||||
}
|
||||
|
||||
const exp = parser.source.readStr(i);
|
||||
parser.source.updateBufferBoundary(i + 1);
|
||||
return buildTagExpObj(exp, parser)
|
||||
}
|
||||
|
||||
function buildTagExpObj(exp, parser){
|
||||
const tagExp = {
|
||||
tagName: "",
|
||||
selfClosing: false
|
||||
};
|
||||
let attrsExp = "";
|
||||
|
||||
// Check for self-closing tag before setting the name
|
||||
if(exp[exp.length -1] === "/") {
|
||||
tagExp.selfClosing = true;
|
||||
exp = exp.slice(0, -1); // Remove the trailing slash
|
||||
}
|
||||
|
||||
//separate tag name
|
||||
let i = 0;
|
||||
for (; i < exp.length; i++) {
|
||||
const char = exp[i];
|
||||
if(char === " "){
|
||||
tagExp.tagName = exp.substring(0, i);
|
||||
attrsExp = exp.substring(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//only tag
|
||||
if(tagExp.tagName.length === 0 && i === exp.length)tagExp.tagName = exp;
|
||||
|
||||
tagExp.tagName = tagExp.tagName.trimEnd();
|
||||
|
||||
if(!parser.options.attributes.ignore && attrsExp.length > 0){
|
||||
parseAttributesExp(attrsExp,parser)
|
||||
}
|
||||
|
||||
return tagExp;
|
||||
}
|
||||
|
||||
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
||||
|
||||
function parseAttributesExp(attrStr, parser) {
|
||||
const matches = getAllMatches(attrStr, attrsRegx);
|
||||
const len = matches.length; //don't make it inline
|
||||
for (let i = 0; i < len; i++) {
|
||||
let attrName = parser.processAttrName(matches[i][1]);
|
||||
let attrVal = parser.replaceEntities(matches[i][4] || true);
|
||||
|
||||
parser.outputBuilder.addAttribute(attrName, attrVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getAllMatches = function(string, regex) {
|
||||
const matches = [];
|
||||
let match = regex.exec(string);
|
||||
while (match) {
|
||||
const allmatches = [];
|
||||
allmatches.startIndex = regex.lastIndex - match[0].length;
|
||||
const len = match.length;
|
||||
for (let index = 0; index < len; index++) {
|
||||
allmatches.push(match[index]);
|
||||
}
|
||||
matches.push(allmatches);
|
||||
match = regex.exec(string);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
|
||||
111
node_modules/fast-xml-parser/src/v6/XmlSpecialTagsReader.js
generated
vendored
Normal file
111
node_modules/fast-xml-parser/src/v6/XmlSpecialTagsReader.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
import {readPiExp} from './XmlPartReader.js';
|
||||
|
||||
export function readCdata(parser){
|
||||
//<![ are already read till this point
|
||||
let str = parser.source.readStr(6); //CDATA[
|
||||
parser.source.updateBufferBoundary(6);
|
||||
|
||||
if(str !== "CDATA[") throw new Error(`Invalid CDATA expression at ${parser.source.line}:${parser.source.cols}`);
|
||||
|
||||
let text = parser.source.readUpto("]]>");
|
||||
parser.outputBuilder.addCdata(text);
|
||||
}
|
||||
export function readPiTag(parser){
|
||||
//<? are already read till this point
|
||||
let tagExp = readPiExp(parser, "?>");
|
||||
if(!tagExp) throw new Error("Invalid Pi Tag expression.");
|
||||
|
||||
if (tagExp.tagName === "?xml") {//TODO: test if tagName is just xml
|
||||
parser.outputBuilder.addDeclaration();
|
||||
} else {
|
||||
parser.outputBuilder.addPi("?"+tagExp.tagName);
|
||||
}
|
||||
}
|
||||
|
||||
export function readComment(parser){
|
||||
//<!- are already read till this point
|
||||
let ch = parser.source.readCh();
|
||||
if(ch !== "-") throw new Error(`Invalid comment expression at ${parser.source.line}:${parser.source.cols}`);
|
||||
|
||||
let text = parser.source.readUpto("-->");
|
||||
parser.outputBuilder.addComment(text);
|
||||
}
|
||||
|
||||
const DOCTYPE_tags = {
|
||||
"EL":/^EMENT\s+([^\s>]+)\s+(ANY|EMPTY|\(.+\)\s*$)/m,
|
||||
"AT":/^TLIST\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+$/m,
|
||||
"NO":/^TATION.+$/m
|
||||
}
|
||||
export function readDocType(parser){
|
||||
//<!D are already read till this point
|
||||
let str = parser.source.readStr(6); //OCTYPE
|
||||
parser.source.updateBufferBoundary(6);
|
||||
|
||||
if(str !== "OCTYPE") throw new Error(`Invalid DOCTYPE expression at ${parser.source.line}:${parser.source.cols}`);
|
||||
|
||||
let hasBody = false, lastch = "";
|
||||
|
||||
while(parser.source.canRead()){
|
||||
//TODO: use readChAt like used in partReader
|
||||
let ch = parser.source.readCh();
|
||||
if(hasBody){
|
||||
if (ch === '<') { //Determine the tag type
|
||||
let str = parser.source.readStr(2);
|
||||
parser.source.updateBufferBoundary(2);
|
||||
if(str === "EN"){ //ENTITY
|
||||
let str = parser.source.readStr(4);
|
||||
parser.source.updateBufferBoundary(4);
|
||||
if(str !== "TITY") throw new Error("Invalid DOCTYPE ENTITY expression");
|
||||
|
||||
registerEntity(parser);
|
||||
}else if(str === "!-") {//comment
|
||||
readComment(parser);
|
||||
}else{ //ELEMENT, ATTLIST, NOTATION
|
||||
let dTagExp = parser.source.readUpto(">");
|
||||
const regx = DOCTYPE_tags[str];
|
||||
if(regx){
|
||||
const match = dTagExp.match(regx);
|
||||
if(!match) throw new Error("Invalid DOCTYPE");
|
||||
}else throw new Error("Invalid DOCTYPE");
|
||||
}
|
||||
}else if( ch === '>' && lastch === "]"){//end of doctype
|
||||
return;
|
||||
}
|
||||
}else if( ch === '>'){//end of doctype
|
||||
return;
|
||||
}else if( ch === '['){
|
||||
hasBody = true;
|
||||
}else{
|
||||
lastch = ch;
|
||||
}
|
||||
}//End While loop
|
||||
|
||||
}
|
||||
|
||||
function registerEntity(parser){
|
||||
//read Entity
|
||||
let attrBoundary="";
|
||||
let name ="", val ="";
|
||||
while(source.canRead()){
|
||||
let ch = source.readCh();
|
||||
|
||||
if(attrBoundary){
|
||||
if (ch === attrBoundary){
|
||||
val = text;
|
||||
text = ""
|
||||
}
|
||||
}else if(ch === " " || ch === "\t"){
|
||||
if(!name){
|
||||
name = text.trimStart();
|
||||
text = "";
|
||||
}
|
||||
}else if (ch === '"' || ch === "'") {//start of attrBoundary
|
||||
attrBoundary = ch;
|
||||
}else if(ch === ">"){
|
||||
parser.entityParser.addExternalEntity(name,val);
|
||||
return;
|
||||
}else{
|
||||
text+=ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
node_modules/fast-xml-parser/src/v6/inputSource/BufferSource.js
generated
vendored
Normal file
116
node_modules/fast-xml-parser/src/v6/inputSource/BufferSource.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
const Constants = {
|
||||
space: 32,
|
||||
tab: 9
|
||||
}
|
||||
export default class BufferSource{
|
||||
constructor(bytesArr){
|
||||
this.line = 1;
|
||||
this.cols = 0;
|
||||
this.buffer = bytesArr;
|
||||
this.startIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
readCh() {
|
||||
return String.fromCharCode(this.buffer[this.startIndex++]);
|
||||
}
|
||||
|
||||
readChAt(index) {
|
||||
return String.fromCharCode(this.buffer[this.startIndex+index]);
|
||||
}
|
||||
|
||||
readStr(n,from){
|
||||
if(typeof from === "undefined") from = this.startIndex;
|
||||
return this.buffer.slice(from, from + n).toString();
|
||||
}
|
||||
|
||||
readUpto(stopStr) {
|
||||
const inputLength = this.buffer.length;
|
||||
const stopLength = stopStr.length;
|
||||
const stopBuffer = Buffer.from(stopStr);
|
||||
|
||||
for (let i = this.startIndex; i < inputLength; i++) {
|
||||
let match = true;
|
||||
for (let j = 0; j < stopLength; j++) {
|
||||
if (this.buffer[i + j] !== stopBuffer[j]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const result = this.buffer.slice(this.startIndex, i).toString();
|
||||
this.startIndex = i + stopLength;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected end of source. Reading '${stopStr}'`);
|
||||
}
|
||||
|
||||
readUptoCloseTag(stopStr) { //stopStr: "</tagname"
|
||||
const inputLength = this.buffer.length;
|
||||
const stopLength = stopStr.length;
|
||||
const stopBuffer = Buffer.from(stopStr);
|
||||
let stopIndex = 0;
|
||||
//0: non-matching, 1: matching stop string, 2: matching closing
|
||||
let match = 0;
|
||||
|
||||
for (let i = this.startIndex; i < inputLength; i++) {
|
||||
if(match === 1){//initial part matched
|
||||
if(stopIndex === 0) stopIndex = i;
|
||||
if(this.buffer[i] === Constants.space || this.buffer[i] === Constants.tab) continue;
|
||||
else if(this.buffer[i] === '>'){ //TODO: if it should be equivalent ASCII
|
||||
match = 2;
|
||||
//tag boundary found
|
||||
// this.startIndex
|
||||
}
|
||||
}else{
|
||||
match = 1;
|
||||
for (let j = 0; j < stopLength; j++) {
|
||||
if (this.buffer[i + j] !== stopBuffer[j]) {
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match === 2) {//matched closing part
|
||||
const result = this.buffer.slice(this.startIndex, stopIndex - 1 ).toString();
|
||||
this.startIndex = i + 1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected end of source. Reading '${stopStr}'`);
|
||||
}
|
||||
|
||||
readFromBuffer(n, shouldUpdate) {
|
||||
let ch;
|
||||
if (n === 1) {
|
||||
ch = this.buffer[this.startIndex];
|
||||
if (ch === 10) {
|
||||
this.line++;
|
||||
this.cols = 1;
|
||||
} else {
|
||||
this.cols++;
|
||||
}
|
||||
ch = String.fromCharCode(ch);
|
||||
} else {
|
||||
this.cols += n;
|
||||
ch = this.buffer.slice(this.startIndex, this.startIndex + n).toString();
|
||||
}
|
||||
if (shouldUpdate) this.updateBuffer(n);
|
||||
return ch;
|
||||
}
|
||||
|
||||
updateBufferBoundary(n = 1) { //n: number of characters read
|
||||
this.startIndex += n;
|
||||
}
|
||||
|
||||
canRead(n){
|
||||
n = n || this.startIndex;
|
||||
return this.buffer.length - n + 1 > 0;
|
||||
}
|
||||
|
||||
}
|
||||
121
node_modules/fast-xml-parser/src/v6/inputSource/StringSource.js
generated
vendored
Normal file
121
node_modules/fast-xml-parser/src/v6/inputSource/StringSource.js
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
const whiteSpaces = [" ", "\n", "\t"];
|
||||
|
||||
|
||||
export default class StringSource{
|
||||
constructor(str){
|
||||
this.line = 1;
|
||||
this.cols = 0;
|
||||
this.buffer = str;
|
||||
//a boundary pointer to indicate where from the buffer dat should be read
|
||||
// data before this pointer can be deleted to free the memory
|
||||
this.startIndex = 0;
|
||||
}
|
||||
|
||||
readCh() {
|
||||
return this.buffer[this.startIndex++];
|
||||
}
|
||||
|
||||
readChAt(index) {
|
||||
return this.buffer[this.startIndex+index];
|
||||
}
|
||||
|
||||
readStr(n,from){
|
||||
if(typeof from === "undefined") from = this.startIndex;
|
||||
return this.buffer.substring(from, from + n);
|
||||
}
|
||||
|
||||
readUpto(stopStr) {
|
||||
const inputLength = this.buffer.length;
|
||||
const stopLength = stopStr.length;
|
||||
|
||||
for (let i = this.startIndex; i < inputLength; i++) {
|
||||
let match = true;
|
||||
for (let j = 0; j < stopLength; j++) {
|
||||
if (this.buffer[i + j] !== stopStr[j]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const result = this.buffer.substring(this.startIndex, i);
|
||||
this.startIndex = i + stopLength;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected end of source. Reading '${stopStr}'`);
|
||||
}
|
||||
|
||||
readUptoCloseTag(stopStr) { //stopStr: "</tagname"
|
||||
const inputLength = this.buffer.length;
|
||||
const stopLength = stopStr.length;
|
||||
let stopIndex = 0;
|
||||
//0: non-matching, 1: matching stop string, 2: matching closing
|
||||
let match = 0;
|
||||
|
||||
for (let i = this.startIndex; i < inputLength; i++) {
|
||||
if(match === 1){//initial part matched
|
||||
if(stopIndex === 0) stopIndex = i;
|
||||
if(this.buffer[i] === ' ' || this.buffer[i] === '\t') continue;
|
||||
else if(this.buffer[i] === '>'){
|
||||
match = 2;
|
||||
//tag boundary found
|
||||
// this.startIndex
|
||||
}
|
||||
}else{
|
||||
match = 1;
|
||||
for (let j = 0; j < stopLength; j++) {
|
||||
if (this.buffer[i + j] !== stopStr[j]) {
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match === 2) {//matched closing part
|
||||
const result = this.buffer.substring(this.startIndex, stopIndex - 1 );
|
||||
this.startIndex = i + 1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected end of source. Reading '${stopStr}'`);
|
||||
}
|
||||
|
||||
readFromBuffer(n, updateIndex){
|
||||
let ch;
|
||||
if(n===1){
|
||||
ch = this.buffer[this.startIndex];
|
||||
// if(ch === "\n") {
|
||||
// this.line++;
|
||||
// this.cols = 1;
|
||||
// }else{
|
||||
// this.cols++;
|
||||
// }
|
||||
}else{
|
||||
ch = this.buffer.substring(this.startIndex, this.startIndex + n);
|
||||
// if("".indexOf("\n") !== -1){
|
||||
// //TODO: handle the scenario when there are multiple lines
|
||||
// //TODO: col should be set to number of chars after last '\n'
|
||||
// // this.cols = 1;
|
||||
// }else{
|
||||
// this.cols += n;
|
||||
|
||||
// }
|
||||
}
|
||||
if(updateIndex) this.updateBufferBoundary(n);
|
||||
return ch;
|
||||
}
|
||||
|
||||
//TODO: rename to updateBufferReadIndex
|
||||
|
||||
updateBufferBoundary(n = 1) { //n: number of characters read
|
||||
this.startIndex += n;
|
||||
}
|
||||
|
||||
canRead(n){
|
||||
n = n || this.startIndex;
|
||||
return this.buffer.length - n + 1 > 0;
|
||||
}
|
||||
|
||||
}
|
||||
105
node_modules/fast-xml-parser/src/v6/valueParsers/EntitiesParser.js
generated
vendored
Normal file
105
node_modules/fast-xml-parser/src/v6/valueParsers/EntitiesParser.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
const ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
||||
const htmlEntities = {
|
||||
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
||||
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
||||
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
||||
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
||||
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
||||
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
||||
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
||||
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
||||
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
||||
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
||||
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
||||
};
|
||||
|
||||
export default class EntitiesParser {
|
||||
constructor(replaceHtmlEntities) {
|
||||
this.replaceHtmlEntities = replaceHtmlEntities;
|
||||
this.docTypeEntities = {};
|
||||
this.lastEntities = {
|
||||
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
||||
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
||||
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
||||
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
||||
};
|
||||
}
|
||||
|
||||
addExternalEntities(externalEntities) {
|
||||
const entKeys = Object.keys(externalEntities);
|
||||
for (let i = 0; i < entKeys.length; i++) {
|
||||
const ent = entKeys[i];
|
||||
this.addExternalEntity(ent, externalEntities[ent])
|
||||
}
|
||||
}
|
||||
addExternalEntity(key, val) {
|
||||
validateEntityName(key);
|
||||
if (val.indexOf("&") !== -1) {
|
||||
reportWarning(`Entity ${key} is not added as '&' is found in value;`)
|
||||
return;
|
||||
} else {
|
||||
this.lastEntities[ent] = {
|
||||
regex: new RegExp("&" + key + ";", "g"),
|
||||
val: val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addDocTypeEntities(entities) {
|
||||
const entKeys = Object.keys(entities);
|
||||
for (let i = 0; i < entKeys.length; i++) {
|
||||
const ent = entKeys[i];
|
||||
this.docTypeEntities[ent] = {
|
||||
regex: new RegExp("&" + ent + ";", "g"),
|
||||
val: entities[ent]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return this.replaceEntitiesValue(val)
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Replace DOCTYPE entities
|
||||
* 2. Replace external entities
|
||||
* 3. Replace HTML entities if asked
|
||||
* @param {string} val
|
||||
*/
|
||||
replaceEntitiesValue(val) {
|
||||
if (typeof val === "string" && val.length > 0) {
|
||||
for (let entityName in this.docTypeEntities) {
|
||||
const entity = this.docTypeEntities[entityName];
|
||||
val = val.replace(entity.regx, entity.val);
|
||||
}
|
||||
for (let entityName in this.lastEntities) {
|
||||
const entity = this.lastEntities[entityName];
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
if (this.replaceHtmlEntities) {
|
||||
for (let entityName in htmlEntities) {
|
||||
const entity = htmlEntities[entityName];
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
}
|
||||
val = val.replace(ampEntity.regex, ampEntity.val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
//an entity name should not contains special characters that may be used in regex
|
||||
//Eg !?\\\/[]$%{}^&*()<>
|
||||
const specialChar = "!?\\\/[]$%{}^&*()<>|+";
|
||||
|
||||
function validateEntityName(name) {
|
||||
for (let i = 0; i < specialChar.length; i++) {
|
||||
const ch = specialChar[i];
|
||||
if (name.indexOf(ch) !== -1) throw new Error(`Invalid character ${ch} in entity name`);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
22
node_modules/fast-xml-parser/src/v6/valueParsers/booleanParser.js
generated
vendored
Normal file
22
node_modules/fast-xml-parser/src/v6/valueParsers/booleanParser.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
export default class boolParser{
|
||||
constructor(trueList, falseList){
|
||||
if(trueList)
|
||||
this.trueList = trueList;
|
||||
else
|
||||
this.trueList = ["true"];
|
||||
|
||||
if(falseList)
|
||||
this.falseList = falseList;
|
||||
else
|
||||
this.falseList = ["false"];
|
||||
}
|
||||
parse(val){
|
||||
if (typeof val === 'string') {
|
||||
//TODO: performance: don't convert
|
||||
const temp = val.toLowerCase();
|
||||
if(this.trueList.indexOf(temp) !== -1) return true;
|
||||
else if(this.falseList.indexOf(temp) !== -1 ) return false;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
19
node_modules/fast-xml-parser/src/v6/valueParsers/booleanParserExt.js
generated
vendored
Normal file
19
node_modules/fast-xml-parser/src/v6/valueParsers/booleanParserExt.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
export default function boolParserExt(val){
|
||||
if(isArray(val)){
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
val[i] = parse(val[i])
|
||||
}
|
||||
}else{
|
||||
val = parse(val)
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
function parse(val){
|
||||
if (typeof val === 'string') {
|
||||
const temp = val.toLowerCase();
|
||||
if(temp === 'true' || temp ==="yes" || temp==="1") return true;
|
||||
else if(temp === 'false' || temp ==="no" || temp==="0") return false;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
38
node_modules/fast-xml-parser/src/v6/valueParsers/currency.js
generated
vendored
Normal file
38
node_modules/fast-xml-parser/src/v6/valueParsers/currency.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
const defaultOptions = {
|
||||
maxLength: 200,
|
||||
// locale: "en-IN"
|
||||
}
|
||||
const localeMap = {
|
||||
"$":"en-US",
|
||||
"€":"de-DE",
|
||||
"£":"en-GB",
|
||||
"¥":"ja-JP",
|
||||
"₹":"en-IN",
|
||||
}
|
||||
const sign = "(?:-|\+)?";
|
||||
const digitsAndSeparator = "(?:\d+|\d{1,3}(?:,\d{3})+)";
|
||||
const decimalPart = "(?:\.\d{1,2})?";
|
||||
const symbol = "(?:\$|€|¥|₹)?";
|
||||
|
||||
const currencyCheckRegex = /^\s*(?:-|\+)?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d{1,2})?\s*(?:\$|€|¥|₹)?\s*$/u;
|
||||
|
||||
export default class CurrencyParser{
|
||||
constructor(options){
|
||||
this.options = options || defaultOptions;
|
||||
}
|
||||
parse(val){
|
||||
if (typeof val === 'string' && val.length <= this.options.maxLength) {
|
||||
if(val.indexOf(",,") !== -1 && val.indexOf(".." !== -1)){
|
||||
const match = val.match(currencyCheckRegex);
|
||||
if(match){
|
||||
const locale = this.options.locale || localeMap[match[2]||match[5]||"₹"];
|
||||
const formatter = new Intl.NumberFormat(locale)
|
||||
val = val.replace(/[^0-9,.]/g, '').trim();
|
||||
val = Number(val.replace(formatter.format(1000)[1], ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
CurrencyParser.defaultOptions = defaultOptions;
|
||||
13
node_modules/fast-xml-parser/src/v6/valueParsers/join.js
generated
vendored
Normal file
13
node_modules/fast-xml-parser/src/v6/valueParsers/join.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
*
|
||||
* @param {array} val
|
||||
* @param {string} by
|
||||
* @returns
|
||||
*/
|
||||
export default function join(val, by=" "){
|
||||
if(isArray(val)){
|
||||
val.join(by)
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
14
node_modules/fast-xml-parser/src/v6/valueParsers/number.js
generated
vendored
Normal file
14
node_modules/fast-xml-parser/src/v6/valueParsers/number.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import toNumber from "strnum";
|
||||
|
||||
|
||||
export default class numParser{
|
||||
constructor(options){
|
||||
this.options = options;
|
||||
}
|
||||
parse(val){
|
||||
if (typeof val === 'string') {
|
||||
val = toNumber(val,this.options);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
6
node_modules/fast-xml-parser/src/v6/valueParsers/trim.js
generated
vendored
Normal file
6
node_modules/fast-xml-parser/src/v6/valueParsers/trim.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export default class trimmer{
|
||||
parse(val){
|
||||
if(typeof val === "string") return val.trim();
|
||||
else return val;
|
||||
}
|
||||
}
|
||||
425
node_modules/fast-xml-parser/src/validator.js
generated
vendored
Normal file
425
node_modules/fast-xml-parser/src/validator.js
generated
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
'use strict';
|
||||
|
||||
import { getAllMatches, isName } from './util.js';
|
||||
|
||||
const defaultOptions = {
|
||||
allowBooleanAttributes: false, //A tag can have attributes without any value
|
||||
unpairedTags: []
|
||||
};
|
||||
|
||||
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
||||
export function validate(xmlData, options) {
|
||||
options = Object.assign({}, defaultOptions, options);
|
||||
|
||||
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
||||
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
||||
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
|
||||
const tags = [];
|
||||
let tagFound = false;
|
||||
|
||||
//indicates that the root tag has been closed (aka. depth 0 has been reached)
|
||||
let reachedRoot = false;
|
||||
|
||||
if (xmlData[0] === '\ufeff') {
|
||||
// check for byte order mark (BOM)
|
||||
xmlData = xmlData.substr(1);
|
||||
}
|
||||
|
||||
for (let i = 0; i < xmlData.length; i++) {
|
||||
|
||||
if (xmlData[i] === '<' && xmlData[i + 1] === '?') {
|
||||
i += 2;
|
||||
i = readPI(xmlData, i);
|
||||
if (i.err) return i;
|
||||
} else if (xmlData[i] === '<') {
|
||||
//starting of tag
|
||||
//read until you reach to '>' avoiding any '>' in attribute value
|
||||
let tagStartPos = i;
|
||||
i++;
|
||||
|
||||
if (xmlData[i] === '!') {
|
||||
i = readCommentAndCDATA(xmlData, i);
|
||||
continue;
|
||||
} else {
|
||||
let closingTag = false;
|
||||
if (xmlData[i] === '/') {
|
||||
//closing tag
|
||||
closingTag = true;
|
||||
i++;
|
||||
}
|
||||
//read tagname
|
||||
let tagName = '';
|
||||
for (; i < xmlData.length &&
|
||||
xmlData[i] !== '>' &&
|
||||
xmlData[i] !== ' ' &&
|
||||
xmlData[i] !== '\t' &&
|
||||
xmlData[i] !== '\n' &&
|
||||
xmlData[i] !== '\r'; i++
|
||||
) {
|
||||
tagName += xmlData[i];
|
||||
}
|
||||
tagName = tagName.trim();
|
||||
//console.log(tagName);
|
||||
|
||||
if (tagName[tagName.length - 1] === '/') {
|
||||
//self closing tag without attributes
|
||||
tagName = tagName.substring(0, tagName.length - 1);
|
||||
//continue;
|
||||
i--;
|
||||
}
|
||||
if (!validateTagName(tagName)) {
|
||||
let msg;
|
||||
if (tagName.trim().length === 0) {
|
||||
msg = "Invalid space after '<'.";
|
||||
} else {
|
||||
msg = "Tag '" + tagName + "' is an invalid name.";
|
||||
}
|
||||
return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
|
||||
}
|
||||
|
||||
const result = readAttributeStr(xmlData, i);
|
||||
if (result === false) {
|
||||
return getErrorObject('InvalidAttr', "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i));
|
||||
}
|
||||
let attrStr = result.value;
|
||||
i = result.index;
|
||||
|
||||
if (attrStr[attrStr.length - 1] === '/') {
|
||||
//self closing tag
|
||||
const attrStrStart = i - attrStr.length;
|
||||
attrStr = attrStr.substring(0, attrStr.length - 1);
|
||||
const isValid = validateAttributeString(attrStr, options);
|
||||
if (isValid === true) {
|
||||
tagFound = true;
|
||||
//continue; //text may presents after self closing tag
|
||||
} else {
|
||||
//the result from the nested function returns the position of the error within the attribute
|
||||
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
||||
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
||||
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
|
||||
}
|
||||
} else if (closingTag) {
|
||||
if (!result.tagClosed) {
|
||||
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
||||
} else if (attrStr.trim().length > 0) {
|
||||
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
||||
} else if (tags.length === 0) {
|
||||
return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
||||
} else {
|
||||
const otg = tags.pop();
|
||||
if (tagName !== otg.tagName) {
|
||||
let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
|
||||
return getErrorObject('InvalidTag',
|
||||
"Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ", col " + openPos.col + ") instead of closing tag '" + tagName + "'.",
|
||||
getLineNumberForPosition(xmlData, tagStartPos));
|
||||
}
|
||||
|
||||
//when there are no more tags, we reached the root level.
|
||||
if (tags.length == 0) {
|
||||
reachedRoot = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const isValid = validateAttributeString(attrStr, options);
|
||||
if (isValid !== true) {
|
||||
//the result from the nested function returns the position of the error within the attribute
|
||||
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
||||
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
||||
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
||||
}
|
||||
|
||||
//if the root level has been reached before ...
|
||||
if (reachedRoot === true) {
|
||||
return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
|
||||
} else if (options.unpairedTags.indexOf(tagName) !== -1) {
|
||||
//don't push into stack
|
||||
} else {
|
||||
tags.push({ tagName, tagStartPos });
|
||||
}
|
||||
tagFound = true;
|
||||
}
|
||||
|
||||
//skip tag text value
|
||||
//It may include comments and CDATA value
|
||||
for (i++; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === '<') {
|
||||
if (xmlData[i + 1] === '!') {
|
||||
//comment or CADATA
|
||||
i++;
|
||||
i = readCommentAndCDATA(xmlData, i);
|
||||
continue;
|
||||
} else if (xmlData[i + 1] === '?') {
|
||||
i = readPI(xmlData, ++i);
|
||||
if (i.err) return i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (xmlData[i] === '&') {
|
||||
const afterAmp = validateAmpersand(xmlData, i);
|
||||
if (afterAmp == -1)
|
||||
return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
|
||||
i = afterAmp;
|
||||
} else {
|
||||
if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
|
||||
return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
|
||||
}
|
||||
}
|
||||
} //end of reading tag text value
|
||||
if (xmlData[i] === '<') {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isWhiteSpace(xmlData[i])) {
|
||||
continue;
|
||||
}
|
||||
return getErrorObject('InvalidChar', "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!tagFound) {
|
||||
return getErrorObject('InvalidXml', 'Start tag expected.', 1);
|
||||
} else if (tags.length == 1) {
|
||||
return getErrorObject('InvalidTag', "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
|
||||
} else if (tags.length > 0) {
|
||||
return getErrorObject('InvalidXml', "Invalid '" +
|
||||
JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\r?\n/g, '') +
|
||||
"' found.", { line: 1, col: 1 });
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
function isWhiteSpace(char) {
|
||||
return char === ' ' || char === '\t' || char === '\n' || char === '\r';
|
||||
}
|
||||
/**
|
||||
* Read Processing insstructions and skip
|
||||
* @param {*} xmlData
|
||||
* @param {*} i
|
||||
*/
|
||||
function readPI(xmlData, i) {
|
||||
const start = i;
|
||||
for (; i < xmlData.length; i++) {
|
||||
if (xmlData[i] == '?' || xmlData[i] == ' ') {
|
||||
//tagname
|
||||
const tagname = xmlData.substr(start, i - start);
|
||||
if (i > 5 && tagname === 'xml') {
|
||||
return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
|
||||
} else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
|
||||
//check if valid attribut string
|
||||
i++;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function readCommentAndCDATA(xmlData, i) {
|
||||
if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
|
||||
//comment
|
||||
for (i += 3; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
|
||||
i += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
xmlData.length > i + 8 &&
|
||||
xmlData[i + 1] === 'D' &&
|
||||
xmlData[i + 2] === 'O' &&
|
||||
xmlData[i + 3] === 'C' &&
|
||||
xmlData[i + 4] === 'T' &&
|
||||
xmlData[i + 5] === 'Y' &&
|
||||
xmlData[i + 6] === 'P' &&
|
||||
xmlData[i + 7] === 'E'
|
||||
) {
|
||||
let angleBracketsCount = 1;
|
||||
for (i += 8; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === '<') {
|
||||
angleBracketsCount++;
|
||||
} else if (xmlData[i] === '>') {
|
||||
angleBracketsCount--;
|
||||
if (angleBracketsCount === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
xmlData.length > i + 9 &&
|
||||
xmlData[i + 1] === '[' &&
|
||||
xmlData[i + 2] === 'C' &&
|
||||
xmlData[i + 3] === 'D' &&
|
||||
xmlData[i + 4] === 'A' &&
|
||||
xmlData[i + 5] === 'T' &&
|
||||
xmlData[i + 6] === 'A' &&
|
||||
xmlData[i + 7] === '['
|
||||
) {
|
||||
for (i += 8; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
|
||||
i += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
const doubleQuote = '"';
|
||||
const singleQuote = "'";
|
||||
|
||||
/**
|
||||
* Keep reading xmlData until '<' is found outside the attribute value.
|
||||
* @param {string} xmlData
|
||||
* @param {number} i
|
||||
*/
|
||||
function readAttributeStr(xmlData, i) {
|
||||
let attrStr = '';
|
||||
let startChar = '';
|
||||
let tagClosed = false;
|
||||
for (; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
|
||||
if (startChar === '') {
|
||||
startChar = xmlData[i];
|
||||
} else if (startChar !== xmlData[i]) {
|
||||
//if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
|
||||
} else {
|
||||
startChar = '';
|
||||
}
|
||||
} else if (xmlData[i] === '>') {
|
||||
if (startChar === '') {
|
||||
tagClosed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
attrStr += xmlData[i];
|
||||
}
|
||||
if (startChar !== '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
value: attrStr,
|
||||
index: i,
|
||||
tagClosed: tagClosed
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all the attributes whether valid or invalid.
|
||||
*/
|
||||
const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
||||
|
||||
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
||||
|
||||
function validateAttributeString(attrStr, options) {
|
||||
//console.log("start:"+attrStr+":end");
|
||||
|
||||
//if(attrStr.trim().length === 0) return true; //empty string
|
||||
|
||||
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
||||
const attrNames = {};
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
if (matches[i][1].length === 0) {
|
||||
//nospace before attribute name: a="sd"b="saf"
|
||||
return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]))
|
||||
} else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
|
||||
return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
|
||||
} else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
|
||||
//independent attribute: ab
|
||||
return getErrorObject('InvalidAttr', "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
|
||||
}
|
||||
/* else if(matches[i][6] === undefined){//attribute without value: ab=
|
||||
return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
|
||||
} */
|
||||
const attrName = matches[i][2];
|
||||
if (!validateAttrName(attrName)) {
|
||||
return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {
|
||||
//check for duplicate attribute.
|
||||
attrNames[attrName] = 1;
|
||||
} else {
|
||||
return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateNumberAmpersand(xmlData, i) {
|
||||
let re = /\d/;
|
||||
if (xmlData[i] === 'x') {
|
||||
i++;
|
||||
re = /[\da-fA-F]/;
|
||||
}
|
||||
for (; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === ';')
|
||||
return i;
|
||||
if (!xmlData[i].match(re))
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function validateAmpersand(xmlData, i) {
|
||||
// https://www.w3.org/TR/xml/#dt-charref
|
||||
i++;
|
||||
if (xmlData[i] === ';')
|
||||
return -1;
|
||||
if (xmlData[i] === '#') {
|
||||
i++;
|
||||
return validateNumberAmpersand(xmlData, i);
|
||||
}
|
||||
let count = 0;
|
||||
for (; i < xmlData.length; i++, count++) {
|
||||
if (xmlData[i].match(/\w/) && count < 20)
|
||||
continue;
|
||||
if (xmlData[i] === ';')
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function getErrorObject(code, message, lineNumber) {
|
||||
return {
|
||||
err: {
|
||||
code: code,
|
||||
msg: message,
|
||||
line: lineNumber.line || lineNumber,
|
||||
col: lineNumber.col,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function validateAttrName(attrName) {
|
||||
return isName(attrName);
|
||||
}
|
||||
|
||||
// const startsWithXML = /^xml/i;
|
||||
|
||||
function validateTagName(tagname) {
|
||||
return isName(tagname) /* && !tagname.match(startsWithXML) */;
|
||||
}
|
||||
|
||||
//this function returns the line number for the character at the given index
|
||||
function getLineNumberForPosition(xmlData, index) {
|
||||
const lines = xmlData.substring(0, index).split(/\r?\n/);
|
||||
return {
|
||||
line: lines.length,
|
||||
|
||||
// column number is last line's length + 1, because column numbering starts at 1:
|
||||
col: lines[lines.length - 1].length + 1
|
||||
};
|
||||
}
|
||||
|
||||
//this function returns the position of the first character of match within attrStr
|
||||
function getPositionFromMatch(match) {
|
||||
return match.startIndex + match[1].length;
|
||||
}
|
||||
6
node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js
generated
vendored
Normal file
6
node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Re-export from fast-xml-builder for backward compatibility
|
||||
import XMLBuilder from 'fast-xml-builder';
|
||||
export default XMLBuilder;
|
||||
|
||||
// If there are any named exports you also want to re-export:
|
||||
export * from 'fast-xml-builder';
|
||||
410
node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
generated
vendored
Normal file
410
node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
generated
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
import { isName } from '../util.js';
|
||||
|
||||
export default class DocTypeReader {
|
||||
constructor(options) {
|
||||
this.suppressValidationErr = !options;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
readDocType(xmlData, i) {
|
||||
const entities = Object.create(null);
|
||||
let entityCount = 0;
|
||||
|
||||
if (xmlData[i + 3] === 'O' &&
|
||||
xmlData[i + 4] === 'C' &&
|
||||
xmlData[i + 5] === 'T' &&
|
||||
xmlData[i + 6] === 'Y' &&
|
||||
xmlData[i + 7] === 'P' &&
|
||||
xmlData[i + 8] === 'E') {
|
||||
i = i + 9;
|
||||
let angleBracketsCount = 1;
|
||||
let hasBody = false, comment = false;
|
||||
let exp = "";
|
||||
for (; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === '<' && !comment) { //Determine the tag type
|
||||
if (hasBody && hasSeq(xmlData, "!ENTITY", i)) {
|
||||
i += 7;
|
||||
let entityName, val;
|
||||
[entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);
|
||||
if (val.indexOf("&") === -1) { //Parameter entities are not supported
|
||||
if (this.options.enabled !== false &&
|
||||
this.options.maxEntityCount != null &&
|
||||
entityCount >= this.options.maxEntityCount) {
|
||||
throw new Error(
|
||||
`Entity count (${entityCount + 1}) exceeds maximum allowed (${this.options.maxEntityCount})`
|
||||
);
|
||||
}
|
||||
//const escaped = entityName.replace(/[.\-+*:]/g, '\\.');
|
||||
const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
entities[entityName] = {
|
||||
regx: RegExp(`&${escaped};`, "g"),
|
||||
val: val
|
||||
};
|
||||
entityCount++;
|
||||
}
|
||||
}
|
||||
else if (hasBody && hasSeq(xmlData, "!ELEMENT", i)) {
|
||||
i += 8;//Not supported
|
||||
const { index } = this.readElementExp(xmlData, i + 1);
|
||||
i = index;
|
||||
} else if (hasBody && hasSeq(xmlData, "!ATTLIST", i)) {
|
||||
i += 8;//Not supported
|
||||
// const {index} = this.readAttlistExp(xmlData,i+1);
|
||||
// i = index;
|
||||
} else if (hasBody && hasSeq(xmlData, "!NOTATION", i)) {
|
||||
i += 9;//Not supported
|
||||
const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr);
|
||||
i = index;
|
||||
} else if (hasSeq(xmlData, "!--", i)) comment = true;
|
||||
else throw new Error(`Invalid DOCTYPE`);
|
||||
|
||||
angleBracketsCount++;
|
||||
exp = "";
|
||||
} else if (xmlData[i] === '>') { //Read tag content
|
||||
if (comment) {
|
||||
if (xmlData[i - 1] === "-" && xmlData[i - 2] === "-") {
|
||||
comment = false;
|
||||
angleBracketsCount--;
|
||||
}
|
||||
} else {
|
||||
angleBracketsCount--;
|
||||
}
|
||||
if (angleBracketsCount === 0) {
|
||||
break;
|
||||
}
|
||||
} else if (xmlData[i] === '[') {
|
||||
hasBody = true;
|
||||
} else {
|
||||
exp += xmlData[i];
|
||||
}
|
||||
}
|
||||
if (angleBracketsCount !== 0) {
|
||||
throw new Error(`Unclosed DOCTYPE`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Invalid Tag instead of DOCTYPE`);
|
||||
}
|
||||
return { entities, i };
|
||||
}
|
||||
readEntityExp(xmlData, i) {
|
||||
//External entities are not supported
|
||||
// <!ENTITY ext SYSTEM "http://normal-website.com" >
|
||||
|
||||
//Parameter entities are not supported
|
||||
// <!ENTITY entityname "&anotherElement;">
|
||||
|
||||
//Internal entities are supported
|
||||
// <!ENTITY entityname "replacement text">
|
||||
|
||||
// Skip leading whitespace after <!ENTITY
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read entity name
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
|
||||
i++;
|
||||
}
|
||||
let entityName = xmlData.substring(startIndex, i);
|
||||
|
||||
validateEntityName(entityName);
|
||||
|
||||
// Skip whitespace after entity name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Check for unsupported constructs (external entities or parameter entities)
|
||||
if (!this.suppressValidationErr) {
|
||||
if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") {
|
||||
throw new Error("External entities are not supported");
|
||||
} else if (xmlData[i] === "%") {
|
||||
throw new Error("Parameter entities are not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// Read entity value (internal entity)
|
||||
let entityValue = "";
|
||||
[i, entityValue] = this.readIdentifierVal(xmlData, i, "entity");
|
||||
|
||||
// Validate entity size
|
||||
if (this.options.enabled !== false &&
|
||||
this.options.maxEntitySize != null &&
|
||||
entityValue.length > this.options.maxEntitySize) {
|
||||
throw new Error(
|
||||
`Entity "${entityName}" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`
|
||||
);
|
||||
}
|
||||
|
||||
i--;
|
||||
return [entityName, entityValue, i];
|
||||
}
|
||||
|
||||
readNotationExp(xmlData, i) {
|
||||
// Skip leading whitespace after <!NOTATION
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read notation name
|
||||
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
i++;
|
||||
}
|
||||
let notationName = xmlData.substring(startIndex, i);
|
||||
|
||||
!this.suppressValidationErr && validateEntityName(notationName);
|
||||
|
||||
// Skip whitespace after notation name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Check identifier type (SYSTEM or PUBLIC)
|
||||
const identifierType = xmlData.substring(i, i + 6).toUpperCase();
|
||||
if (!this.suppressValidationErr && identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
|
||||
throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
|
||||
}
|
||||
i += identifierType.length;
|
||||
|
||||
// Skip whitespace after identifier type
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read public identifier (if PUBLIC)
|
||||
let publicIdentifier = null;
|
||||
let systemIdentifier = null;
|
||||
|
||||
if (identifierType === "PUBLIC") {
|
||||
[i, publicIdentifier] = this.readIdentifierVal(xmlData, i, "publicIdentifier");
|
||||
|
||||
// Skip whitespace after public identifier
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Optionally read system identifier
|
||||
if (xmlData[i] === '"' || xmlData[i] === "'") {
|
||||
[i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
|
||||
}
|
||||
} else if (identifierType === "SYSTEM") {
|
||||
// Read system identifier (mandatory for SYSTEM)
|
||||
[i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
|
||||
|
||||
if (!this.suppressValidationErr && !systemIdentifier) {
|
||||
throw new Error("Missing mandatory system identifier for SYSTEM notation");
|
||||
}
|
||||
}
|
||||
|
||||
return { notationName, publicIdentifier, systemIdentifier, index: --i };
|
||||
}
|
||||
|
||||
readIdentifierVal(xmlData, i, type) {
|
||||
let identifierVal = "";
|
||||
const startChar = xmlData[i];
|
||||
if (startChar !== '"' && startChar !== "'") {
|
||||
throw new Error(`Expected quoted string, found "${startChar}"`);
|
||||
}
|
||||
i++;
|
||||
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && xmlData[i] !== startChar) {
|
||||
i++;
|
||||
}
|
||||
identifierVal = xmlData.substring(startIndex, i);
|
||||
|
||||
if (xmlData[i] !== startChar) {
|
||||
throw new Error(`Unterminated ${type} value`);
|
||||
}
|
||||
i++;
|
||||
return [i, identifierVal];
|
||||
}
|
||||
|
||||
readElementExp(xmlData, i) {
|
||||
// <!ELEMENT br EMPTY>
|
||||
// <!ELEMENT div ANY>
|
||||
// <!ELEMENT title (#PCDATA)>
|
||||
// <!ELEMENT book (title, author+)>
|
||||
// <!ELEMENT name (content-model)>
|
||||
|
||||
// Skip leading whitespace after <!ELEMENT
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read element name
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
i++;
|
||||
}
|
||||
let elementName = xmlData.substring(startIndex, i);
|
||||
|
||||
// Validate element name
|
||||
if (!this.suppressValidationErr && !isName(elementName)) {
|
||||
throw new Error(`Invalid element name: "${elementName}"`);
|
||||
}
|
||||
|
||||
// Skip whitespace after element name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
let contentModel = "";
|
||||
// Expect '(' to start content model
|
||||
if (xmlData[i] === "E" && hasSeq(xmlData, "MPTY", i)) i += 4;
|
||||
else if (xmlData[i] === "A" && hasSeq(xmlData, "NY", i)) i += 2;
|
||||
else if (xmlData[i] === "(") {
|
||||
i++; // Move past '('
|
||||
|
||||
// Read content model
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && xmlData[i] !== ")") {
|
||||
i++;
|
||||
}
|
||||
contentModel = xmlData.substring(startIndex, i);
|
||||
|
||||
if (xmlData[i] !== ")") {
|
||||
throw new Error("Unterminated content model");
|
||||
}
|
||||
|
||||
} else if (!this.suppressValidationErr) {
|
||||
throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`);
|
||||
}
|
||||
|
||||
return {
|
||||
elementName,
|
||||
contentModel: contentModel.trim(),
|
||||
index: i
|
||||
};
|
||||
}
|
||||
|
||||
readAttlistExp(xmlData, i) {
|
||||
// Skip leading whitespace after <!ATTLIST
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read element name
|
||||
let startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
i++;
|
||||
}
|
||||
let elementName = xmlData.substring(startIndex, i);
|
||||
|
||||
// Validate element name
|
||||
validateEntityName(elementName)
|
||||
|
||||
// Skip whitespace after element name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read attribute name
|
||||
startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
i++;
|
||||
}
|
||||
let attributeName = xmlData.substring(startIndex, i);
|
||||
|
||||
// Validate attribute name
|
||||
if (!validateEntityName(attributeName)) {
|
||||
throw new Error(`Invalid attribute name: "${attributeName}"`);
|
||||
}
|
||||
|
||||
// Skip whitespace after attribute name
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read attribute type
|
||||
let attributeType = "";
|
||||
if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") {
|
||||
attributeType = "NOTATION";
|
||||
i += 8; // Move past "NOTATION"
|
||||
|
||||
// Skip whitespace after "NOTATION"
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Expect '(' to start the list of notations
|
||||
if (xmlData[i] !== "(") {
|
||||
throw new Error(`Expected '(', found "${xmlData[i]}"`);
|
||||
}
|
||||
i++; // Move past '('
|
||||
|
||||
// Read the list of allowed notations
|
||||
let allowedNotations = [];
|
||||
while (i < xmlData.length && xmlData[i] !== ")") {
|
||||
|
||||
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
|
||||
i++;
|
||||
}
|
||||
let notation = xmlData.substring(startIndex, i);
|
||||
|
||||
// Validate notation name
|
||||
notation = notation.trim();
|
||||
if (!validateEntityName(notation)) {
|
||||
throw new Error(`Invalid notation name: "${notation}"`);
|
||||
}
|
||||
|
||||
allowedNotations.push(notation);
|
||||
|
||||
// Skip '|' separator or exit loop
|
||||
if (xmlData[i] === "|") {
|
||||
i++; // Move past '|'
|
||||
i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|'
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlData[i] !== ")") {
|
||||
throw new Error("Unterminated list of notations");
|
||||
}
|
||||
i++; // Move past ')'
|
||||
|
||||
// Store the allowed notations as part of the attribute type
|
||||
attributeType += " (" + allowedNotations.join("|") + ")";
|
||||
} else {
|
||||
// Handle simple types (e.g., CDATA, ID, IDREF, etc.)
|
||||
const startIndex = i;
|
||||
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
||||
i++;
|
||||
}
|
||||
attributeType += xmlData.substring(startIndex, i);
|
||||
|
||||
// Validate simple attribute type
|
||||
const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
|
||||
if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {
|
||||
throw new Error(`Invalid attribute type: "${attributeType}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip whitespace after attribute type
|
||||
i = skipWhitespace(xmlData, i);
|
||||
|
||||
// Read default value
|
||||
let defaultValue = "";
|
||||
if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") {
|
||||
defaultValue = "#REQUIRED";
|
||||
i += 8;
|
||||
} else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") {
|
||||
defaultValue = "#IMPLIED";
|
||||
i += 7;
|
||||
} else {
|
||||
[i, defaultValue] = this.readIdentifierVal(xmlData, i, "ATTLIST");
|
||||
}
|
||||
|
||||
return {
|
||||
elementName,
|
||||
attributeName,
|
||||
attributeType,
|
||||
defaultValue,
|
||||
index: i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const skipWhitespace = (data, index) => {
|
||||
while (index < data.length && /\s/.test(data[index])) {
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
|
||||
|
||||
function hasSeq(data, seq, i) {
|
||||
for (let j = 0; j < seq.length; j++) {
|
||||
if (seq[j] !== data[i + j + 1]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateEntityName(name) {
|
||||
if (isName(name))
|
||||
return name;
|
||||
else
|
||||
throw new Error(`Invalid entity name ${name}`);
|
||||
}
|
||||
159
node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js
generated
vendored
Normal file
159
node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from "../util.js";
|
||||
|
||||
const defaultOnDangerousProperty = (name) => {
|
||||
if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
|
||||
return "__" + name;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
|
||||
export const defaultOptions = {
|
||||
preserveOrder: false,
|
||||
attributeNamePrefix: '@_',
|
||||
attributesGroupName: false,
|
||||
textNodeName: '#text',
|
||||
ignoreAttributes: true,
|
||||
removeNSPrefix: false, // remove NS from tag name or attribute name if true
|
||||
allowBooleanAttributes: false, //a tag can have attributes without any value
|
||||
//ignoreRootElement : false,
|
||||
parseTagValue: true,
|
||||
parseAttributeValue: false,
|
||||
trimValues: true, //Trim string values of tag and attributes
|
||||
cdataPropName: false,
|
||||
numberParseOptions: {
|
||||
hex: true,
|
||||
leadingZeros: true,
|
||||
eNotation: true
|
||||
},
|
||||
tagValueProcessor: function (tagName, val) {
|
||||
return val;
|
||||
},
|
||||
attributeValueProcessor: function (attrName, val) {
|
||||
return val;
|
||||
},
|
||||
stopNodes: [], //nested tags will not be parsed even for errors
|
||||
alwaysCreateTextNode: false,
|
||||
isArray: () => false,
|
||||
commentPropName: false,
|
||||
unpairedTags: [],
|
||||
processEntities: true,
|
||||
htmlEntities: false,
|
||||
ignoreDeclaration: false,
|
||||
ignorePiTags: false,
|
||||
transformTagName: false,
|
||||
transformAttributeName: false,
|
||||
updateTag: function (tagName, jPath, attrs) {
|
||||
return tagName
|
||||
},
|
||||
// skipEmptyListItem: false
|
||||
captureMetaData: false,
|
||||
maxNestedTags: 100,
|
||||
strictReservedNames: true,
|
||||
jPath: true, // if true, pass jPath string to callbacks; if false, pass matcher instance
|
||||
onDangerousProperty: defaultOnDangerousProperty
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Validates that a property name is safe to use
|
||||
* @param {string} propertyName - The property name to validate
|
||||
* @param {string} optionName - The option field name (for error message)
|
||||
* @throws {Error} If property name is dangerous
|
||||
*/
|
||||
function validatePropertyName(propertyName, optionName) {
|
||||
if (typeof propertyName !== 'string') {
|
||||
return; // Only validate string property names
|
||||
}
|
||||
|
||||
const normalized = propertyName.toLowerCase();
|
||||
if (DANGEROUS_PROPERTY_NAMES.some(dangerous => normalized === dangerous.toLowerCase())) {
|
||||
throw new Error(
|
||||
`[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
|
||||
);
|
||||
}
|
||||
|
||||
if (criticalProperties.some(dangerous => normalized === dangerous.toLowerCase())) {
|
||||
throw new Error(
|
||||
`[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes processEntities option for backward compatibility
|
||||
* @param {boolean|object} value
|
||||
* @returns {object} Always returns normalized object
|
||||
*/
|
||||
function normalizeProcessEntities(value) {
|
||||
// Boolean backward compatibility
|
||||
if (typeof value === 'boolean') {
|
||||
return {
|
||||
enabled: value, // true or false
|
||||
maxEntitySize: 10000,
|
||||
maxExpansionDepth: 10,
|
||||
maxTotalExpansions: 1000,
|
||||
maxExpandedLength: 100000,
|
||||
maxEntityCount: 100,
|
||||
allowedTags: null,
|
||||
tagFilter: null
|
||||
};
|
||||
}
|
||||
|
||||
// Object config - merge with defaults
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return {
|
||||
enabled: value.enabled !== false,
|
||||
maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),
|
||||
maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10),
|
||||
maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1000),
|
||||
maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
|
||||
maxEntityCount: Math.max(1, value.maxEntityCount ?? 100),
|
||||
allowedTags: value.allowedTags ?? null,
|
||||
tagFilter: value.tagFilter ?? null
|
||||
};
|
||||
}
|
||||
|
||||
// Default to enabled with limits
|
||||
return normalizeProcessEntities(true);
|
||||
}
|
||||
|
||||
export const buildOptions = function (options) {
|
||||
const built = Object.assign({}, defaultOptions, options);
|
||||
|
||||
// Validate property names to prevent prototype pollution
|
||||
const propertyNameOptions = [
|
||||
{ value: built.attributeNamePrefix, name: 'attributeNamePrefix' },
|
||||
{ value: built.attributesGroupName, name: 'attributesGroupName' },
|
||||
{ value: built.textNodeName, name: 'textNodeName' },
|
||||
{ value: built.cdataPropName, name: 'cdataPropName' },
|
||||
{ value: built.commentPropName, name: 'commentPropName' }
|
||||
];
|
||||
|
||||
for (const { value, name } of propertyNameOptions) {
|
||||
if (value) {
|
||||
validatePropertyName(value, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (built.onDangerousProperty === null) {
|
||||
built.onDangerousProperty = defaultOnDangerousProperty;
|
||||
}
|
||||
|
||||
// Always normalize processEntities for backward compatibility and validation
|
||||
built.processEntities = normalizeProcessEntities(built.processEntities);
|
||||
|
||||
// Convert old-style stopNodes for backward compatibility
|
||||
if (built.stopNodes && Array.isArray(built.stopNodes)) {
|
||||
built.stopNodes = built.stopNodes.map(node => {
|
||||
if (typeof node === 'string' && node.startsWith('*.')) {
|
||||
// Old syntax: *.tagname meant "tagname anywhere"
|
||||
// Convert to new syntax: ..tagname
|
||||
return '..' + node.substring(2);
|
||||
}
|
||||
return node;
|
||||
});
|
||||
}
|
||||
//console.debug(built.processEntities)
|
||||
return built;
|
||||
};
|
||||
911
node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js
generated
vendored
Normal file
911
node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js
generated
vendored
Normal file
@@ -0,0 +1,911 @@
|
||||
'use strict';
|
||||
///@ts-check
|
||||
|
||||
import { getAllMatches, isExist, DANGEROUS_PROPERTY_NAMES, criticalProperties } from '../util.js';
|
||||
import xmlNode from './xmlNode.js';
|
||||
import DocTypeReader from './DocTypeReader.js';
|
||||
import toNumber from "strnum";
|
||||
import getIgnoreAttributesFn from "../ignoreAttributes.js";
|
||||
import { Expression, Matcher } from 'path-expression-matcher';
|
||||
|
||||
// const regx =
|
||||
// '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
|
||||
// .replace(/NAME/g, util.nameRegexp);
|
||||
|
||||
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
|
||||
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
|
||||
|
||||
// Helper functions for attribute and namespace handling
|
||||
|
||||
/**
|
||||
* Extract raw attributes (without prefix) from prefixed attribute map
|
||||
* @param {object} prefixedAttrs - Attributes with prefix from buildAttributesMap
|
||||
* @param {object} options - Parser options containing attributeNamePrefix
|
||||
* @returns {object} Raw attributes for matcher
|
||||
*/
|
||||
function extractRawAttributes(prefixedAttrs, options) {
|
||||
if (!prefixedAttrs) return {};
|
||||
|
||||
// Handle attributesGroupName option
|
||||
const attrs = options.attributesGroupName
|
||||
? prefixedAttrs[options.attributesGroupName]
|
||||
: prefixedAttrs;
|
||||
|
||||
if (!attrs) return {};
|
||||
|
||||
const rawAttrs = {};
|
||||
for (const key in attrs) {
|
||||
// Remove the attribute prefix to get raw name
|
||||
if (key.startsWith(options.attributeNamePrefix)) {
|
||||
const rawName = key.substring(options.attributeNamePrefix.length);
|
||||
rawAttrs[rawName] = attrs[key];
|
||||
} else {
|
||||
// Attribute without prefix (shouldn't normally happen, but be safe)
|
||||
rawAttrs[key] = attrs[key];
|
||||
}
|
||||
}
|
||||
return rawAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract namespace from raw tag name
|
||||
* @param {string} rawTagName - Tag name possibly with namespace (e.g., "soap:Envelope")
|
||||
* @returns {string|undefined} Namespace or undefined
|
||||
*/
|
||||
function extractNamespace(rawTagName) {
|
||||
if (!rawTagName || typeof rawTagName !== 'string') return undefined;
|
||||
|
||||
const colonIndex = rawTagName.indexOf(':');
|
||||
if (colonIndex !== -1 && colonIndex > 0) {
|
||||
const ns = rawTagName.substring(0, colonIndex);
|
||||
// Don't treat xmlns as a namespace
|
||||
if (ns !== 'xmlns') {
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export default class OrderedObjParser {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.currentNode = null;
|
||||
this.tagsNodeStack = [];
|
||||
this.docTypeEntities = {};
|
||||
this.lastEntities = {
|
||||
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
||||
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
||||
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
||||
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
||||
};
|
||||
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
||||
this.htmlEntities = {
|
||||
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
||||
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
||||
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
||||
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
||||
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
||||
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
||||
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
||||
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
||||
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
||||
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
||||
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
||||
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
||||
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
||||
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => fromCodePoint(str, 10, "&#") },
|
||||
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => fromCodePoint(str, 16, "&#x") },
|
||||
};
|
||||
this.addExternalEntities = addExternalEntities;
|
||||
this.parseXml = parseXml;
|
||||
this.parseTextData = parseTextData;
|
||||
this.resolveNameSpace = resolveNameSpace;
|
||||
this.buildAttributesMap = buildAttributesMap;
|
||||
this.isItStopNode = isItStopNode;
|
||||
this.replaceEntitiesValue = replaceEntitiesValue;
|
||||
this.readStopNodeData = readStopNodeData;
|
||||
this.saveTextToParentTag = saveTextToParentTag;
|
||||
this.addChild = addChild;
|
||||
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
|
||||
this.entityExpansionCount = 0;
|
||||
this.currentExpandedLength = 0;
|
||||
|
||||
// Initialize path matcher for path-expression-matcher
|
||||
this.matcher = new Matcher();
|
||||
|
||||
// Live read-only proxy of matcher — PEM creates and caches this internally.
|
||||
// All user callbacks receive this instead of the mutable matcher.
|
||||
this.readonlyMatcher = this.matcher.readOnly();
|
||||
|
||||
// Flag to track if current node is a stop node (optimization)
|
||||
this.isCurrentNodeStopNode = false;
|
||||
|
||||
// Pre-compile stopNodes expressions
|
||||
if (this.options.stopNodes && this.options.stopNodes.length > 0) {
|
||||
this.stopNodeExpressions = [];
|
||||
for (let i = 0; i < this.options.stopNodes.length; i++) {
|
||||
const stopNodeExp = this.options.stopNodes[i];
|
||||
if (typeof stopNodeExp === 'string') {
|
||||
// Convert string to Expression object
|
||||
this.stopNodeExpressions.push(new Expression(stopNodeExp));
|
||||
} else if (stopNodeExp instanceof Expression) {
|
||||
// Already an Expression object
|
||||
this.stopNodeExpressions.push(stopNodeExp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function addExternalEntities(externalEntities) {
|
||||
const entKeys = Object.keys(externalEntities);
|
||||
for (let i = 0; i < entKeys.length; i++) {
|
||||
const ent = entKeys[i];
|
||||
const escaped = ent.replace(/[.\-+*:]/g, '\\.');
|
||||
this.lastEntities[ent] = {
|
||||
regex: new RegExp("&" + escaped + ";", "g"),
|
||||
val: externalEntities[ent]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} val
|
||||
* @param {string} tagName
|
||||
* @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath
|
||||
* @param {boolean} dontTrim
|
||||
* @param {boolean} hasAttributes
|
||||
* @param {boolean} isLeafNode
|
||||
* @param {boolean} escapeEntities
|
||||
*/
|
||||
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
|
||||
if (val !== undefined) {
|
||||
if (this.options.trimValues && !dontTrim) {
|
||||
val = val.trim();
|
||||
}
|
||||
if (val.length > 0) {
|
||||
if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
|
||||
|
||||
// Pass jPath string or matcher based on options.jPath setting
|
||||
const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
|
||||
const newval = this.options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);
|
||||
if (newval === null || newval === undefined) {
|
||||
//don't parse
|
||||
return val;
|
||||
} else if (typeof newval !== typeof val || newval !== val) {
|
||||
//overwrite
|
||||
return newval;
|
||||
} else if (this.options.trimValues) {
|
||||
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
||||
} else {
|
||||
const trimmedVal = val.trim();
|
||||
if (trimmedVal === val) {
|
||||
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveNameSpace(tagname) {
|
||||
if (this.options.removeNSPrefix) {
|
||||
const tags = tagname.split(':');
|
||||
const prefix = tagname.charAt(0) === '/' ? '/' : '';
|
||||
if (tags[0] === 'xmlns') {
|
||||
return '';
|
||||
}
|
||||
if (tags.length === 2) {
|
||||
tagname = prefix + tags[1];
|
||||
}
|
||||
}
|
||||
return tagname;
|
||||
}
|
||||
|
||||
//TODO: change regex to capture NS
|
||||
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
|
||||
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
||||
|
||||
function buildAttributesMap(attrStr, jPath, tagName) {
|
||||
if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') {
|
||||
// attrStr = attrStr.replace(/\r?\n/g, ' ');
|
||||
//attrStr = attrStr || attrStr.trim();
|
||||
|
||||
const matches = getAllMatches(attrStr, attrsRegx);
|
||||
const len = matches.length; //don't make it inline
|
||||
const attrs = {};
|
||||
|
||||
// First pass: parse all attributes and update matcher with raw values
|
||||
// This ensures the matcher has all attribute values when processors run
|
||||
const rawAttrsForMatcher = {};
|
||||
for (let i = 0; i < len; i++) {
|
||||
const attrName = this.resolveNameSpace(matches[i][1]);
|
||||
const oldVal = matches[i][4];
|
||||
|
||||
if (attrName.length && oldVal !== undefined) {
|
||||
let parsedVal = oldVal;
|
||||
if (this.options.trimValues) {
|
||||
parsedVal = parsedVal.trim();
|
||||
}
|
||||
parsedVal = this.replaceEntitiesValue(parsedVal, tagName, this.readonlyMatcher);
|
||||
rawAttrsForMatcher[attrName] = parsedVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Update matcher with raw attribute values BEFORE running processors
|
||||
if (Object.keys(rawAttrsForMatcher).length > 0 && typeof jPath === 'object' && jPath.updateCurrent) {
|
||||
jPath.updateCurrent(rawAttrsForMatcher);
|
||||
}
|
||||
|
||||
// Second pass: now process attributes with matcher having full attribute context
|
||||
for (let i = 0; i < len; i++) {
|
||||
const attrName = this.resolveNameSpace(matches[i][1]);
|
||||
|
||||
// Convert jPath to string if needed for ignoreAttributesFn
|
||||
const jPathStr = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
|
||||
if (this.ignoreAttributesFn(attrName, jPathStr)) {
|
||||
continue
|
||||
}
|
||||
|
||||
let oldVal = matches[i][4];
|
||||
let aName = this.options.attributeNamePrefix + attrName;
|
||||
|
||||
if (attrName.length) {
|
||||
if (this.options.transformAttributeName) {
|
||||
aName = this.options.transformAttributeName(aName);
|
||||
}
|
||||
//if (aName === "__proto__") aName = "#__proto__";
|
||||
aName = sanitizeName(aName, this.options);
|
||||
|
||||
if (oldVal !== undefined) {
|
||||
if (this.options.trimValues) {
|
||||
oldVal = oldVal.trim();
|
||||
}
|
||||
oldVal = this.replaceEntitiesValue(oldVal, tagName, this.readonlyMatcher);
|
||||
|
||||
// Pass jPath string or readonlyMatcher based on options.jPath setting
|
||||
const jPathOrMatcher = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
|
||||
const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPathOrMatcher);
|
||||
if (newVal === null || newVal === undefined) {
|
||||
//don't parse
|
||||
attrs[aName] = oldVal;
|
||||
} else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {
|
||||
//overwrite
|
||||
attrs[aName] = newVal;
|
||||
} else {
|
||||
//parse
|
||||
attrs[aName] = parseValue(
|
||||
oldVal,
|
||||
this.options.parseAttributeValue,
|
||||
this.options.numberParseOptions
|
||||
);
|
||||
}
|
||||
} else if (this.options.allowBooleanAttributes) {
|
||||
attrs[aName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.keys(attrs).length) {
|
||||
return;
|
||||
}
|
||||
if (this.options.attributesGroupName) {
|
||||
const attrCollection = {};
|
||||
attrCollection[this.options.attributesGroupName] = attrs;
|
||||
return attrCollection;
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
}
|
||||
|
||||
const parseXml = function (xmlData) {
|
||||
xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
|
||||
const xmlObj = new xmlNode('!xml');
|
||||
let currentNode = xmlObj;
|
||||
let textData = "";
|
||||
|
||||
// Reset matcher for new document
|
||||
this.matcher.reset();
|
||||
|
||||
// Reset entity expansion counters for this document
|
||||
this.entityExpansionCount = 0;
|
||||
this.currentExpandedLength = 0;
|
||||
|
||||
const docTypeReader = new DocTypeReader(this.options.processEntities);
|
||||
for (let i = 0; i < xmlData.length; i++) {//for each char in XML data
|
||||
const ch = xmlData[i];
|
||||
if (ch === '<') {
|
||||
// const nextIndex = i+1;
|
||||
// const _2ndChar = xmlData[nextIndex];
|
||||
if (xmlData[i + 1] === '/') {//Closing Tag
|
||||
const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
|
||||
let tagName = xmlData.substring(i + 2, closeIndex).trim();
|
||||
|
||||
if (this.options.removeNSPrefix) {
|
||||
const colonIndex = tagName.indexOf(":");
|
||||
if (colonIndex !== -1) {
|
||||
tagName = tagName.substr(colonIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
tagName = transformTagName(this.options.transformTagName, tagName, "", this.options).tagName;
|
||||
|
||||
if (currentNode) {
|
||||
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
|
||||
}
|
||||
|
||||
//check if last tag of nested tag was unpaired tag
|
||||
const lastTagName = this.matcher.getCurrentTag();
|
||||
if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) {
|
||||
throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
|
||||
}
|
||||
if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) {
|
||||
// Pop the unpaired tag
|
||||
this.matcher.pop();
|
||||
this.tagsNodeStack.pop();
|
||||
}
|
||||
// Pop the closing tag
|
||||
this.matcher.pop();
|
||||
this.isCurrentNodeStopNode = false; // Reset flag when closing tag
|
||||
|
||||
currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
|
||||
textData = "";
|
||||
i = closeIndex;
|
||||
} else if (xmlData[i + 1] === '?') {
|
||||
|
||||
let tagData = readTagExp(xmlData, i, false, "?>");
|
||||
if (!tagData) throw new Error("Pi Tag is not closed.");
|
||||
|
||||
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
|
||||
if ((this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags) {
|
||||
//do nothing
|
||||
} else {
|
||||
|
||||
const childNode = new xmlNode(tagData.tagName);
|
||||
childNode.add(this.options.textNodeName, "");
|
||||
|
||||
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
|
||||
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
|
||||
}
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, i);
|
||||
}
|
||||
|
||||
|
||||
i = tagData.closeIndex + 1;
|
||||
} else if (xmlData.substr(i + 1, 3) === '!--') {
|
||||
const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.")
|
||||
if (this.options.commentPropName) {
|
||||
const comment = xmlData.substring(i + 4, endIndex - 2);
|
||||
|
||||
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
|
||||
|
||||
currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]);
|
||||
}
|
||||
i = endIndex;
|
||||
} else if (xmlData.substr(i + 1, 2) === '!D') {
|
||||
const result = docTypeReader.readDocType(xmlData, i);
|
||||
this.docTypeEntities = result.entities;
|
||||
i = result.i;
|
||||
} else if (xmlData.substr(i + 1, 2) === '![') {
|
||||
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
||||
const tagExp = xmlData.substring(i + 9, closeIndex);
|
||||
|
||||
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
|
||||
|
||||
let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);
|
||||
if (val == undefined) val = "";
|
||||
|
||||
//cdata should be set even if it is 0 length string
|
||||
if (this.options.cdataPropName) {
|
||||
currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]);
|
||||
} else {
|
||||
currentNode.add(this.options.textNodeName, val);
|
||||
}
|
||||
|
||||
i = closeIndex + 2;
|
||||
} else {//Opening tag
|
||||
let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
|
||||
|
||||
// Safety check: readTagExp can return undefined
|
||||
if (!result) {
|
||||
// Log context for debugging
|
||||
const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlData.length, i + 50));
|
||||
throw new Error(`readTagExp returned undefined at position ${i}. Context: "${context}"`);
|
||||
}
|
||||
|
||||
let tagName = result.tagName;
|
||||
const rawTagName = result.rawTagName;
|
||||
let tagExp = result.tagExp;
|
||||
let attrExpPresent = result.attrExpPresent;
|
||||
let closeIndex = result.closeIndex;
|
||||
|
||||
({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
|
||||
|
||||
if (this.options.strictReservedNames &&
|
||||
(tagName === this.options.commentPropName
|
||||
|| tagName === this.options.cdataPropName
|
||||
|| tagName === this.options.textNodeName
|
||||
|| tagName === this.options.attributesGroupName
|
||||
)) {
|
||||
throw new Error(`Invalid tag name: ${tagName}`);
|
||||
}
|
||||
|
||||
//save text as child node
|
||||
if (currentNode && textData) {
|
||||
if (currentNode.tagname !== '!xml') {
|
||||
//when nested tag is found
|
||||
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);
|
||||
}
|
||||
}
|
||||
|
||||
//check if last tag was unpaired tag
|
||||
const lastTag = currentNode;
|
||||
if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) {
|
||||
currentNode = this.tagsNodeStack.pop();
|
||||
this.matcher.pop();
|
||||
}
|
||||
|
||||
// Clean up self-closing syntax BEFORE processing attributes
|
||||
// This is where tagExp gets the trailing / removed
|
||||
let isSelfClosing = false;
|
||||
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
||||
isSelfClosing = true;
|
||||
if (tagName[tagName.length - 1] === "/") {
|
||||
tagName = tagName.substr(0, tagName.length - 1);
|
||||
tagExp = tagName;
|
||||
} else {
|
||||
tagExp = tagExp.substr(0, tagExp.length - 1);
|
||||
}
|
||||
|
||||
// Re-check attrExpPresent after cleaning
|
||||
attrExpPresent = (tagName !== tagExp);
|
||||
}
|
||||
|
||||
// Now process attributes with CLEAN tagExp (no trailing /)
|
||||
let prefixedAttrs = null;
|
||||
let rawAttrs = {};
|
||||
let namespace = undefined;
|
||||
|
||||
// Extract namespace from rawTagName
|
||||
namespace = extractNamespace(rawTagName);
|
||||
|
||||
// Push tag to matcher FIRST (with empty attrs for now) so callbacks see correct path
|
||||
if (tagName !== xmlObj.tagname) {
|
||||
this.matcher.push(tagName, {}, namespace);
|
||||
}
|
||||
|
||||
// Now build attributes - callbacks will see correct matcher state
|
||||
if (tagName !== tagExp && attrExpPresent) {
|
||||
// Build attributes (returns prefixed attributes for the tree)
|
||||
// Note: buildAttributesMap now internally updates the matcher with raw attributes
|
||||
prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
|
||||
|
||||
if (prefixedAttrs) {
|
||||
// Extract raw attributes (without prefix) for our use
|
||||
rawAttrs = extractRawAttributes(prefixedAttrs, this.options);
|
||||
}
|
||||
}
|
||||
|
||||
// Now check if this is a stop node (after attributes are set)
|
||||
if (tagName !== xmlObj.tagname) {
|
||||
this.isCurrentNodeStopNode = this.isItStopNode(this.stopNodeExpressions, this.matcher);
|
||||
}
|
||||
|
||||
const startIndex = i;
|
||||
if (this.isCurrentNodeStopNode) {
|
||||
let tagContent = "";
|
||||
|
||||
// For self-closing tags, content is empty
|
||||
if (isSelfClosing) {
|
||||
i = result.closeIndex;
|
||||
}
|
||||
//unpaired tag
|
||||
else if (this.options.unpairedTags.indexOf(tagName) !== -1) {
|
||||
i = result.closeIndex;
|
||||
}
|
||||
//normal tag
|
||||
else {
|
||||
//read until closing tag is found
|
||||
const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
|
||||
if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
|
||||
i = result.i;
|
||||
tagContent = result.tagContent;
|
||||
}
|
||||
|
||||
const childNode = new xmlNode(tagName);
|
||||
|
||||
if (prefixedAttrs) {
|
||||
childNode[":@"] = prefixedAttrs;
|
||||
}
|
||||
|
||||
// For stop nodes, store raw content as-is without any processing
|
||||
childNode.add(this.options.textNodeName, tagContent);
|
||||
|
||||
this.matcher.pop(); // Pop the stop node tag
|
||||
this.isCurrentNodeStopNode = false; // Reset flag
|
||||
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
} else {
|
||||
//selfClosing tag
|
||||
if (isSelfClosing) {
|
||||
({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
|
||||
|
||||
const childNode = new xmlNode(tagName);
|
||||
if (prefixedAttrs) {
|
||||
childNode[":@"] = prefixedAttrs;
|
||||
}
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
this.matcher.pop(); // Pop self-closing tag
|
||||
this.isCurrentNodeStopNode = false; // Reset flag
|
||||
}
|
||||
else if (this.options.unpairedTags.indexOf(tagName) !== -1) {//unpaired tag
|
||||
const childNode = new xmlNode(tagName);
|
||||
if (prefixedAttrs) {
|
||||
childNode[":@"] = prefixedAttrs;
|
||||
}
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
this.matcher.pop(); // Pop unpaired tag
|
||||
this.isCurrentNodeStopNode = false; // Reset flag
|
||||
i = result.closeIndex;
|
||||
// Continue to next iteration without changing currentNode
|
||||
continue;
|
||||
}
|
||||
//opening tag
|
||||
else {
|
||||
const childNode = new xmlNode(tagName);
|
||||
if (this.tagsNodeStack.length > this.options.maxNestedTags) {
|
||||
throw new Error("Maximum nested tags exceeded");
|
||||
}
|
||||
this.tagsNodeStack.push(currentNode);
|
||||
|
||||
if (prefixedAttrs) {
|
||||
childNode[":@"] = prefixedAttrs;
|
||||
}
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
currentNode = childNode;
|
||||
}
|
||||
textData = "";
|
||||
i = closeIndex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
textData += xmlData[i];
|
||||
}
|
||||
}
|
||||
return xmlObj.child;
|
||||
}
|
||||
|
||||
function addChild(currentNode, childNode, matcher, startIndex) {
|
||||
// unset startIndex if not requested
|
||||
if (!this.options.captureMetaData) startIndex = undefined;
|
||||
|
||||
// Pass jPath string or matcher based on options.jPath setting
|
||||
const jPathOrMatcher = this.options.jPath ? matcher.toString() : matcher;
|
||||
const result = this.options.updateTag(childNode.tagname, jPathOrMatcher, childNode[":@"])
|
||||
if (result === false) {
|
||||
//do nothing
|
||||
} else if (typeof result === "string") {
|
||||
childNode.tagname = result
|
||||
currentNode.addChild(childNode, startIndex);
|
||||
} else {
|
||||
currentNode.addChild(childNode, startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} val - Entity object with regex and val properties
|
||||
* @param {string} tagName - Tag name
|
||||
* @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath
|
||||
*/
|
||||
function replaceEntitiesValue(val, tagName, jPath) {
|
||||
const entityConfig = this.options.processEntities;
|
||||
|
||||
if (!entityConfig || !entityConfig.enabled) {
|
||||
return val;
|
||||
}
|
||||
|
||||
// Check if tag is allowed to contain entities
|
||||
if (entityConfig.allowedTags) {
|
||||
const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
|
||||
const allowed = Array.isArray(entityConfig.allowedTags)
|
||||
? entityConfig.allowedTags.includes(tagName)
|
||||
: entityConfig.allowedTags(tagName, jPathOrMatcher);
|
||||
|
||||
if (!allowed) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply custom tag filter if provided
|
||||
if (entityConfig.tagFilter) {
|
||||
const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
|
||||
if (!entityConfig.tagFilter(tagName, jPathOrMatcher)) {
|
||||
return val; // Skip based on custom filter
|
||||
}
|
||||
}
|
||||
|
||||
// Replace DOCTYPE entities
|
||||
for (const entityName of Object.keys(this.docTypeEntities)) {
|
||||
const entity = this.docTypeEntities[entityName];
|
||||
const matches = val.match(entity.regx);
|
||||
|
||||
if (matches) {
|
||||
// Track expansions
|
||||
this.entityExpansionCount += matches.length;
|
||||
|
||||
// Check expansion limit
|
||||
if (entityConfig.maxTotalExpansions &&
|
||||
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
||||
throw new Error(
|
||||
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
||||
);
|
||||
}
|
||||
|
||||
// Store length before replacement
|
||||
const lengthBefore = val.length;
|
||||
val = val.replace(entity.regx, entity.val);
|
||||
|
||||
// Check expanded length immediately after replacement
|
||||
if (entityConfig.maxExpandedLength) {
|
||||
this.currentExpandedLength += (val.length - lengthBefore);
|
||||
|
||||
if (this.currentExpandedLength > entityConfig.maxExpandedLength) {
|
||||
throw new Error(
|
||||
`Total expanded content size exceeded: ${this.currentExpandedLength} > ${entityConfig.maxExpandedLength}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replace standard entities
|
||||
for (const entityName of Object.keys(this.lastEntities)) {
|
||||
const entity = this.lastEntities[entityName];
|
||||
const matches = val.match(entity.regex);
|
||||
if (matches) {
|
||||
this.entityExpansionCount += matches.length;
|
||||
if (entityConfig.maxTotalExpansions &&
|
||||
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
||||
throw new Error(
|
||||
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
||||
);
|
||||
}
|
||||
}
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
if (val.indexOf('&') === -1) return val;
|
||||
|
||||
// Replace HTML entities if enabled
|
||||
if (this.options.htmlEntities) {
|
||||
for (const entityName of Object.keys(this.htmlEntities)) {
|
||||
const entity = this.htmlEntities[entityName];
|
||||
const matches = val.match(entity.regex);
|
||||
if (matches) {
|
||||
//console.log(matches);
|
||||
this.entityExpansionCount += matches.length;
|
||||
if (entityConfig.maxTotalExpansions &&
|
||||
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
||||
throw new Error(
|
||||
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
||||
);
|
||||
}
|
||||
}
|
||||
val = val.replace(entity.regex, entity.val);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace ampersand entity last
|
||||
val = val.replace(this.ampEntity.regex, this.ampEntity.val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
function saveTextToParentTag(textData, parentNode, matcher, isLeafNode) {
|
||||
if (textData) { //store previously collected data as textNode
|
||||
if (isLeafNode === undefined) isLeafNode = parentNode.child.length === 0
|
||||
|
||||
textData = this.parseTextData(textData,
|
||||
parentNode.tagname,
|
||||
matcher,
|
||||
false,
|
||||
parentNode[":@"] ? Object.keys(parentNode[":@"]).length !== 0 : false,
|
||||
isLeafNode);
|
||||
|
||||
if (textData !== undefined && textData !== "")
|
||||
parentNode.add(this.options.textNodeName, textData);
|
||||
textData = "";
|
||||
}
|
||||
return textData;
|
||||
}
|
||||
|
||||
//TODO: use jPath to simplify the logic
|
||||
/**
|
||||
* @param {Array<Expression>} stopNodeExpressions - Array of compiled Expression objects
|
||||
* @param {Matcher} matcher - Current path matcher
|
||||
*/
|
||||
function isItStopNode(stopNodeExpressions, matcher) {
|
||||
if (!stopNodeExpressions || stopNodeExpressions.length === 0) return false;
|
||||
|
||||
for (let i = 0; i < stopNodeExpressions.length; i++) {
|
||||
if (matcher.matches(stopNodeExpressions[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tag Expression and where it is ending handling single-double quotes situation
|
||||
* @param {string} xmlData
|
||||
* @param {number} i starting index
|
||||
* @returns
|
||||
*/
|
||||
function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
||||
let attrBoundary;
|
||||
let tagExp = "";
|
||||
for (let index = i; index < xmlData.length; index++) {
|
||||
let ch = xmlData[index];
|
||||
if (attrBoundary) {
|
||||
if (ch === attrBoundary) attrBoundary = "";//reset
|
||||
} else if (ch === '"' || ch === "'") {
|
||||
attrBoundary = ch;
|
||||
} else if (ch === closingChar[0]) {
|
||||
if (closingChar[1]) {
|
||||
if (xmlData[index + 1] === closingChar[1]) {
|
||||
return {
|
||||
data: tagExp,
|
||||
index: index
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
data: tagExp,
|
||||
index: index
|
||||
}
|
||||
}
|
||||
} else if (ch === '\t') {
|
||||
ch = " "
|
||||
}
|
||||
tagExp += ch;
|
||||
}
|
||||
}
|
||||
|
||||
function findClosingIndex(xmlData, str, i, errMsg) {
|
||||
const closingIndex = xmlData.indexOf(str, i);
|
||||
if (closingIndex === -1) {
|
||||
throw new Error(errMsg)
|
||||
} else {
|
||||
return closingIndex + str.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
|
||||
const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
|
||||
if (!result) return;
|
||||
let tagExp = result.data;
|
||||
const closeIndex = result.index;
|
||||
const separatorIndex = tagExp.search(/\s/);
|
||||
let tagName = tagExp;
|
||||
let attrExpPresent = true;
|
||||
if (separatorIndex !== -1) {//separate tag name and attributes expression
|
||||
tagName = tagExp.substring(0, separatorIndex);
|
||||
tagExp = tagExp.substring(separatorIndex + 1).trimStart();
|
||||
}
|
||||
|
||||
const rawTagName = tagName;
|
||||
if (removeNSPrefix) {
|
||||
const colonIndex = tagName.indexOf(":");
|
||||
if (colonIndex !== -1) {
|
||||
tagName = tagName.substr(colonIndex + 1);
|
||||
attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tagName: tagName,
|
||||
tagExp: tagExp,
|
||||
closeIndex: closeIndex,
|
||||
attrExpPresent: attrExpPresent,
|
||||
rawTagName: rawTagName,
|
||||
}
|
||||
}
|
||||
/**
|
||||
* find paired tag for a stop node
|
||||
* @param {string} xmlData
|
||||
* @param {string} tagName
|
||||
* @param {number} i
|
||||
*/
|
||||
function readStopNodeData(xmlData, tagName, i) {
|
||||
const startIndex = i;
|
||||
// Starting at 1 since we already have an open tag
|
||||
let openTagCount = 1;
|
||||
|
||||
for (; i < xmlData.length; i++) {
|
||||
if (xmlData[i] === "<") {
|
||||
if (xmlData[i + 1] === "/") {//close tag
|
||||
const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
|
||||
let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
|
||||
if (closeTagName === tagName) {
|
||||
openTagCount--;
|
||||
if (openTagCount === 0) {
|
||||
return {
|
||||
tagContent: xmlData.substring(startIndex, i),
|
||||
i: closeIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
i = closeIndex;
|
||||
} else if (xmlData[i + 1] === '?') {
|
||||
const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.")
|
||||
i = closeIndex;
|
||||
} else if (xmlData.substr(i + 1, 3) === '!--') {
|
||||
const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.")
|
||||
i = closeIndex;
|
||||
} else if (xmlData.substr(i + 1, 2) === '![') {
|
||||
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
||||
i = closeIndex;
|
||||
} else {
|
||||
const tagData = readTagExp(xmlData, i, '>')
|
||||
|
||||
if (tagData) {
|
||||
const openTagName = tagData && tagData.tagName;
|
||||
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") {
|
||||
openTagCount++;
|
||||
}
|
||||
i = tagData.closeIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//end for loop
|
||||
}
|
||||
|
||||
function parseValue(val, shouldParse, options) {
|
||||
if (shouldParse && typeof val === 'string') {
|
||||
//console.log(options)
|
||||
const newval = val.trim();
|
||||
if (newval === 'true') return true;
|
||||
else if (newval === 'false') return false;
|
||||
else return toNumber(val, options);
|
||||
} else {
|
||||
if (isExist(val)) {
|
||||
return val;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fromCodePoint(str, base, prefix) {
|
||||
const codePoint = Number.parseInt(str, base);
|
||||
|
||||
if (codePoint >= 0 && codePoint <= 0x10FFFF) {
|
||||
return String.fromCodePoint(codePoint);
|
||||
} else {
|
||||
return prefix + str + ";";
|
||||
}
|
||||
}
|
||||
|
||||
function transformTagName(fn, tagName, tagExp, options) {
|
||||
if (fn) {
|
||||
const newTagName = fn(tagName);
|
||||
if (tagExp === tagName) {
|
||||
tagExp = newTagName
|
||||
}
|
||||
tagName = newTagName;
|
||||
}
|
||||
tagName = sanitizeName(tagName, options);
|
||||
return { tagName, tagExp };
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sanitizeName(name, options) {
|
||||
if (criticalProperties.includes(name)) {
|
||||
throw new Error(`[SECURITY] Invalid name: "${name}" is a reserved JavaScript keyword that could cause prototype pollution`);
|
||||
} else if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
|
||||
return options.onDangerousProperty(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
71
node_modules/fast-xml-parser/src/xmlparser/XMLParser.js
generated
vendored
Normal file
71
node_modules/fast-xml-parser/src/xmlparser/XMLParser.js
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import { buildOptions } from './OptionsBuilder.js';
|
||||
import OrderedObjParser from './OrderedObjParser.js';
|
||||
import prettify from './node2json.js';
|
||||
import { validate } from "../validator.js";
|
||||
import XmlNode from './xmlNode.js';
|
||||
|
||||
export default class XMLParser {
|
||||
|
||||
constructor(options) {
|
||||
this.externalEntities = {};
|
||||
this.options = buildOptions(options);
|
||||
|
||||
}
|
||||
/**
|
||||
* Parse XML dats to JS object
|
||||
* @param {string|Uint8Array} xmlData
|
||||
* @param {boolean|Object} validationOption
|
||||
*/
|
||||
parse(xmlData, validationOption) {
|
||||
if (typeof xmlData !== "string" && xmlData.toString) {
|
||||
xmlData = xmlData.toString();
|
||||
} else if (typeof xmlData !== "string") {
|
||||
throw new Error("XML data is accepted in String or Bytes[] form.")
|
||||
}
|
||||
|
||||
if (validationOption) {
|
||||
if (validationOption === true) validationOption = {}; //validate with default options
|
||||
|
||||
const result = validate(xmlData, validationOption);
|
||||
if (result !== true) {
|
||||
throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`)
|
||||
}
|
||||
}
|
||||
const orderedObjParser = new OrderedObjParser(this.options);
|
||||
orderedObjParser.addExternalEntities(this.externalEntities);
|
||||
const orderedResult = orderedObjParser.parseXml(xmlData);
|
||||
if (this.options.preserveOrder || orderedResult === undefined) return orderedResult;
|
||||
else return prettify(orderedResult, this.options, orderedObjParser.matcher, orderedObjParser.readonlyMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Entity which is not by default supported by this library
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
*/
|
||||
addEntity(key, value) {
|
||||
if (value.indexOf("&") !== -1) {
|
||||
throw new Error("Entity value can't have '&'")
|
||||
} else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
|
||||
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'")
|
||||
} else if (value === "&") {
|
||||
throw new Error("An entity with value '&' is not permitted");
|
||||
} else {
|
||||
this.externalEntities[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Symbol that can be used to access the metadata
|
||||
* property on a node.
|
||||
*
|
||||
* If Symbol is not available in the environment, an ordinary property is used
|
||||
* and the name of the property is here returned.
|
||||
*
|
||||
* The XMLMetaData property is only present when `captureMetaData`
|
||||
* is true in the options.
|
||||
*/
|
||||
static getMetaDataSymbol() {
|
||||
return XmlNode.getMetaDataSymbol();
|
||||
}
|
||||
}
|
||||
173
node_modules/fast-xml-parser/src/xmlparser/node2json.js
generated
vendored
Normal file
173
node_modules/fast-xml-parser/src/xmlparser/node2json.js
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
'use strict';
|
||||
|
||||
import XmlNode from './xmlNode.js';
|
||||
import { Matcher } from 'path-expression-matcher';
|
||||
|
||||
const METADATA_SYMBOL = XmlNode.getMetaDataSymbol();
|
||||
|
||||
/**
|
||||
* Helper function to strip attribute prefix from attribute map
|
||||
* @param {object} attrs - Attributes with prefix (e.g., {"@_class": "code"})
|
||||
* @param {string} prefix - Attribute prefix to remove (e.g., "@_")
|
||||
* @returns {object} Attributes without prefix (e.g., {"class": "code"})
|
||||
*/
|
||||
function stripAttributePrefix(attrs, prefix) {
|
||||
if (!attrs || typeof attrs !== 'object') return {};
|
||||
if (!prefix) return attrs;
|
||||
|
||||
const rawAttrs = {};
|
||||
for (const key in attrs) {
|
||||
if (key.startsWith(prefix)) {
|
||||
const rawName = key.substring(prefix.length);
|
||||
rawAttrs[rawName] = attrs[key];
|
||||
} else {
|
||||
// Attribute without prefix (shouldn't normally happen, but be safe)
|
||||
rawAttrs[key] = attrs[key];
|
||||
}
|
||||
}
|
||||
return rawAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {array} node
|
||||
* @param {any} options
|
||||
* @param {Matcher} matcher - Path matcher instance
|
||||
* @returns
|
||||
*/
|
||||
export default function prettify(node, options, matcher, readonlyMatcher) {
|
||||
return compress(node, options, matcher, readonlyMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {array} arr
|
||||
* @param {object} options
|
||||
* @param {Matcher} matcher - Path matcher instance
|
||||
* @returns object
|
||||
*/
|
||||
function compress(arr, options, matcher, readonlyMatcher) {
|
||||
let text;
|
||||
const compressedObj = {}; //This is intended to be a plain object
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const tagObj = arr[i];
|
||||
const property = propName(tagObj);
|
||||
|
||||
// Push current property to matcher WITH RAW ATTRIBUTES (no prefix)
|
||||
if (property !== undefined && property !== options.textNodeName) {
|
||||
const rawAttrs = stripAttributePrefix(
|
||||
tagObj[":@"] || {},
|
||||
options.attributeNamePrefix
|
||||
);
|
||||
matcher.push(property, rawAttrs);
|
||||
}
|
||||
|
||||
if (property === options.textNodeName) {
|
||||
if (text === undefined) text = tagObj[property];
|
||||
else text += "" + tagObj[property];
|
||||
} else if (property === undefined) {
|
||||
continue;
|
||||
} else if (tagObj[property]) {
|
||||
|
||||
let val = compress(tagObj[property], options, matcher, readonlyMatcher);
|
||||
const isLeaf = isLeafTag(val, options);
|
||||
|
||||
if (tagObj[":@"]) {
|
||||
assignAttributes(val, tagObj[":@"], readonlyMatcher, options);
|
||||
} else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {
|
||||
val = val[options.textNodeName];
|
||||
} else if (Object.keys(val).length === 0) {
|
||||
if (options.alwaysCreateTextNode) val[options.textNodeName] = "";
|
||||
else val = "";
|
||||
}
|
||||
|
||||
if (tagObj[METADATA_SYMBOL] !== undefined && typeof val === "object" && val !== null) {
|
||||
val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata
|
||||
}
|
||||
|
||||
|
||||
if (compressedObj[property] !== undefined && Object.prototype.hasOwnProperty.call(compressedObj, property)) {
|
||||
if (!Array.isArray(compressedObj[property])) {
|
||||
compressedObj[property] = [compressedObj[property]];
|
||||
}
|
||||
compressedObj[property].push(val);
|
||||
} else {
|
||||
//TODO: if a node is not an array, then check if it should be an array
|
||||
//also determine if it is a leaf node
|
||||
|
||||
// Pass jPath string or readonlyMatcher based on options.jPath setting
|
||||
const jPathOrMatcher = options.jPath ? readonlyMatcher.toString() : readonlyMatcher;
|
||||
if (options.isArray(property, jPathOrMatcher, isLeaf)) {
|
||||
compressedObj[property] = [val];
|
||||
} else {
|
||||
compressedObj[property] = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Pop property from matcher after processing
|
||||
if (property !== undefined && property !== options.textNodeName) {
|
||||
matcher.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// if(text && text.length > 0) compressedObj[options.textNodeName] = text;
|
||||
if (typeof text === "string") {
|
||||
if (text.length > 0) compressedObj[options.textNodeName] = text;
|
||||
} else if (text !== undefined) compressedObj[options.textNodeName] = text;
|
||||
|
||||
|
||||
return compressedObj;
|
||||
}
|
||||
|
||||
function propName(obj) {
|
||||
const keys = Object.keys(obj);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key !== ":@") return key;
|
||||
}
|
||||
}
|
||||
|
||||
function assignAttributes(obj, attrMap, readonlyMatcher, options) {
|
||||
if (attrMap) {
|
||||
const keys = Object.keys(attrMap);
|
||||
const len = keys.length; //don't make it inline
|
||||
for (let i = 0; i < len; i++) {
|
||||
const atrrName = keys[i]; // This is the PREFIXED name (e.g., "@_class")
|
||||
|
||||
// Strip prefix for matcher path (for isArray callback)
|
||||
const rawAttrName = atrrName.startsWith(options.attributeNamePrefix)
|
||||
? atrrName.substring(options.attributeNamePrefix.length)
|
||||
: atrrName;
|
||||
|
||||
// For attributes, we need to create a temporary path
|
||||
// Pass jPath string or matcher based on options.jPath setting
|
||||
const jPathOrMatcher = options.jPath
|
||||
? readonlyMatcher.toString() + "." + rawAttrName
|
||||
: readonlyMatcher;
|
||||
|
||||
if (options.isArray(atrrName, jPathOrMatcher, true, true)) {
|
||||
obj[atrrName] = [attrMap[atrrName]];
|
||||
} else {
|
||||
obj[atrrName] = attrMap[atrrName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isLeafTag(obj, options) {
|
||||
const { textNodeName } = options;
|
||||
const propCount = Object.keys(obj).length;
|
||||
|
||||
if (propCount === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
propCount === 1 &&
|
||||
(obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
40
node_modules/fast-xml-parser/src/xmlparser/xmlNode.js
generated
vendored
Normal file
40
node_modules/fast-xml-parser/src/xmlparser/xmlNode.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
let METADATA_SYMBOL;
|
||||
|
||||
if (typeof Symbol !== "function") {
|
||||
METADATA_SYMBOL = "@@xmlMetadata";
|
||||
} else {
|
||||
METADATA_SYMBOL = Symbol("XML Node Metadata");
|
||||
}
|
||||
|
||||
export default class XmlNode {
|
||||
constructor(tagname) {
|
||||
this.tagname = tagname;
|
||||
this.child = []; //nested tags, text, cdata, comments in order
|
||||
this[":@"] = Object.create(null); //attributes map
|
||||
}
|
||||
add(key, val) {
|
||||
// this.child.push( {name : key, val: val, isCdata: isCdata });
|
||||
if (key === "__proto__") key = "#__proto__";
|
||||
this.child.push({ [key]: val });
|
||||
}
|
||||
addChild(node, startIndex) {
|
||||
if (node.tagname === "__proto__") node.tagname = "#__proto__";
|
||||
if (node[":@"] && Object.keys(node[":@"]).length > 0) {
|
||||
this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] });
|
||||
} else {
|
||||
this.child.push({ [node.tagname]: node.child });
|
||||
}
|
||||
// if requested, add the startIndex
|
||||
if (startIndex !== undefined) {
|
||||
// Note: for now we just overwrite the metadata. If we had more complex metadata,
|
||||
// we might need to do an object append here: metadata = { ...metadata, startIndex }
|
||||
this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
|
||||
}
|
||||
}
|
||||
/** symbol used for metadata */
|
||||
static getMetaDataSymbol() {
|
||||
return METADATA_SYMBOL;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user