Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

arraybuffer-xml-parser

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

arraybuffer-xml-parser - npm Package Compare versions

Comparing version 0.5.0 to 0.5.1

lib-esm/bufferUtils/arrayIndexOf.d.ts

628

lib/index.js

@@ -1,614 +0,18 @@

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var dynamicTyping = require('dynamic-typing');
const utf8Decoder$1 = new TextDecoder();
const decoder$1 = {
decode: (array) => {
return utf8Decoder$1.decode(array);
},
};
const defaultOptions = {
trimValues: true,
attributeNamePrefix: '$',
attributesNodeName: false,
ignoreAttributes: false,
ignoreNameSpace: false,
allowBooleanAttributes: false,
dynamicTypingAttributeValue: true,
textNodeName: '#text',
dynamicTypingNodeValue: true,
arrayMode: false,
cdataTagName: false,
tagValueProcessor: (value) => {
return decoder$1.decode(value).replace(/\r/g, '');
},
attributeValueProcessor: (value) => value,
stopNodes: [],
};
class XMLNode {
constructor(tagName, parent, value) {
this.tagName = tagName;
this.parent = parent;
this.children = Object.create({}); //child tags
this.attributes = Object.create({}); //attributes map
this.value = value; //text only
this.startIndex = -1;
}
addChild(child) {
if (Array.isArray(this.children[child.tagName])) {
//already presents
this.children[child.tagName].push(child);
} else {
this.children[child.tagName] = [child];
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
}
}
function arrayIndexOf(array, referenceArray, index = 0) {
let found = 0;
let foundIndex = -1;
for (let i = index; i < array.length && found < referenceArray.length; i++) {
if (array[i] === referenceArray[found]) {
if (!found) {
foundIndex = i;
}
found++;
} else {
if (found > 0) {
let j = 0;
for (
;
j <= found && array[foundIndex + j] === array[foundIndex + found];
j++
);
if (j < found + 1) {
foundIndex = -1;
found = 0;
} else {
foundIndex++;
}
} else {
found = 0;
foundIndex = -1;
}
}
}
if (found !== referenceArray.length) {
foundIndex = -1;
}
return foundIndex;
}
function arrayTrim(array) {
let i = 0;
let j = array.length - 1;
for (; i < array.length && array[i] <= 0x20; i++);
for (; j >= i && array[j] <= 0x20; j--);
if (i === 0 && j === array.length - 1) return array;
return array.subarray(i, j + 1);
}
function closingIndexForOpeningTag(data, i) {
let attrBoundary;
let endIndex = 0;
for (let index = i; index < data.length; index++) {
let byte = data[index];
if (attrBoundary) {
if (byte === attrBoundary) attrBoundary = 0; //reset
} else if (byte === 0x22 || byte === 0x27) {
attrBoundary = byte;
} else if (byte === 0x3e) {
return {
data: decoder.decode(data.subarray(i, i + endIndex)),
index,
};
} else if (byte === 0x09) {
byte = 0x20;
}
endIndex++;
}
}
function findClosingIndex(xmlData, str, i, errMsg) {
const closingIndex = arrayIndexOf(xmlData, str, i);
if (closingIndex === -1) {
throw new Error(errMsg);
} else {
return closingIndex + str.length - 1;
}
}
function getAllMatches(string, regex) {
return Array.from(string.matchAll(regex));
}
function isEmptyObject(obj) {
// fastest implementation: https://jsbench.me/qfkqv692c8/1
// eslint-disable-next-line no-unreachable-loop
for (const key in obj) {
return false;
}
return true;
}
/**
* Copy all the properties of a into b.
* @param {object} target
* @param {object} source
*/
function merge(target, source, arrayMode) {
if (!source) return;
for (const key in source) {
if (arrayMode === 'strict') {
target[key] = [source[key]];
} else {
target[key] = source[key];
}
}
}
/**
* Check if a tag name should be treated as array
*
* @param tagName the node tagName
* @param arrayMode the array mode option
* @param parentTagName the parent tag name
* @returns {boolean} true if node should be parsed as array
*/
function isTagNameInArrayMode(tagName, arrayMode, parentTagName) {
if (arrayMode === false) {
return false;
} else if (arrayMode instanceof RegExp) {
return arrayMode.test(tagName);
} else if (typeof arrayMode === 'function') {
return !!arrayMode(tagName, parentTagName);
}
return arrayMode === 'strict';
}
const newLocal = '([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?';
const attrsRegx = new RegExp(newLocal, 'g');
//Attributes are strings so no point in using arrayBuffers here
function parseAttributesString(string, options) {
if (options.ignoreAttributes) {
return;
}
string = string.replace(/\r?\n/g, ' ');
const matches = getAllMatches(string, attrsRegx);
const attributes = {};
for (let match of matches) {
const attrName = resolveNameSpace(match[1], options);
if (attrName.length) {
if (match[4] !== undefined) {
if (options.trimValues) {
match[4] = match[4].trim();
}
match[4] = options.attributeValueProcessor(match[4], attrName);
attributes[attrName] = stringParseValue(
match[4],
options.dynamicTypingAttributeValue,
);
} else if (options.allowBooleanAttributes) {
attributes[attrName] = true;
}
}
}
if (isEmptyObject(attributes)) return;
return attributes;
}
function stringParseValue(value, shouldParse) {
if (shouldParse && typeof value === 'string') {
return dynamicTyping.parseString(value);
} else {
return value === undefined ? '' : value;
}
}
function resolveNameSpace(tagName, options) {
if (options.ignoreNameSpace) {
const tags = tagName.split(':');
const prefix = tagName.charAt(0) === '/' ? '/' : '';
if (tags[0] === 'xmlns') {
return '';
}
if (tags.length === 2) {
tagName = prefix + tags[1];
}
}
return tagName;
}
const utf8Decoder = new TextDecoder();
const decoder = {
decode: (array) => {
return utf8Decoder.decode(array);
},
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
function getTraversable(xmlData, options) {
const traversable = new XMLNode('!xml');
let currentNode = traversable;
let dataSize = 0;
let dataIndex = 0;
for (let i = 0; i < xmlData.length; i++) {
if (xmlData[i] === 0x3c) {
// <
const xmlData1 = xmlData[i + 1];
const xmlData2 = xmlData[i + 2];
if (xmlData1 === 0x2f) {
// </ Closing Tag
const closeIndex = findClosingIndex(
xmlData,
[0x3e], //>
i,
'Closing Tag is not closed.',
);
let tagName = decoder.decode(
arrayTrim(xmlData.subarray(i + 2, closeIndex)),
);
tagName = removeNameSpaceIfNeeded(tagName, options);
if (currentNode) {
const value = options.trimValues
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize))
: xmlData.subarray(dataIndex, dataIndex + dataSize);
if (currentNode.value === undefined) {
currentNode.value = value;
} else {
currentNode.value = concat(currentNode.value, value);
}
}
if (
options.stopNodes.length &&
options.stopNodes.includes(currentNode.tagName)
) {
currentNode.children = [];
if (currentNode.attributes === undefined) {
currentNode.attributes = {};
}
currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i);
}
currentNode = currentNode.parent;
i = closeIndex;
dataSize = 0;
dataIndex = i + 1;
} else if (xmlData1 === 0x3f) {
// <? PI, processing instruction
i = findClosingIndex(xmlData, [0x3f, 0x3e], i, 'Pi Tag is not closed.');
} else if (
//!-- comment
xmlData1 === 0x21 &&
xmlData2 === 0x2d &&
xmlData[i + 3] === 0x2d
) {
i = findClosingIndex(
xmlData,
[0x2d, 0x2d, 0x3e], //-->
i,
'Comment is not closed.',
);
if (currentNode && dataSize !== 0) {
if (currentNode.tagName !== '!xml') {
currentNode.value = concat(
currentNode.value,
options.trimValues
? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex))
: xmlData.subarray(dataIndex, dataSize + dataIndex),
);
}
}
dataSize = 0;
dataIndex = i + 1;
//!D
} else if (xmlData1 === 0x21 && xmlData2 === 0x44) {
// <!D
const closeIndex = findClosingIndex(
xmlData,
[0x3e], //>
i,
'DOCTYPE is not closed.',
);
const tagExp = xmlData.subarray(i, closeIndex);
if (arrayIndexOf(tagExp, [0x5b]) >= 0) {
i = arrayIndexOf(xmlData, [0x5d, 0x3e], i) + 1;
} else {
i = closeIndex;
} //![
} else if (xmlData1 === 0x21 && xmlData2 === 0x5b) {
// <![CDATA[some stuff]]>
const closeIndex =
findClosingIndex(
xmlData,
[0x5d, 0x5d, 0x3e], //]]>
i,
'CDATA is not closed.',
) - 2;
const tagExp = xmlData.subarray(i + 9, closeIndex);
//considerations
//1. CDATA will always have parent node
//2. A tag with CDATA is not a leaf node so it's value would be string type.
if (dataSize !== 0) {
const value = options.trimValues
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize))
: xmlData.subarray(dataIndex, dataIndex + dataSize);
currentNode.value = concat(currentNode.value, value);
}
if (options.cdataTagName) {
//add cdata node
const childNode = new XMLNode(
options.cdataTagName,
currentNode,
tagExp,
);
currentNode.addChild(childNode);
//add rest value to parent node
if (tagExp) {
childNode.value = tagExp;
}
} else {
currentNode.value = concat(currentNode.value, tagExp);
}
i = closeIndex + 2;
dataSize = 0;
dataIndex = i + 1;
} else {
//Opening a normal tag
const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1);
let tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' ');
const closeIndex = parsedOpeningTag.index;
const separatorIndex = tagData.indexOf(' ');
let shouldBuildAttributesMap = true;
let tagName =
separatorIndex >= 0
? tagData.substr(0, separatorIndex).replace(/\s+$/, '')
: tagData;
let tagAttributes =
separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : '';
if (options.ignoreNameSpace) {
const colonIndex = tagName.indexOf(':');
if (colonIndex !== -1) {
tagName = tagName.substr(colonIndex + 1);
shouldBuildAttributesMap =
tagName !== parsedOpeningTag.data.substr(colonIndex + 1);
}
}
//save text to parent node
if (currentNode && dataSize !== 0) {
if (currentNode.tagName !== '!xml') {
currentNode.value = concat(
currentNode.value,
options.trimValues
? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize))
: xmlData.subarray(dataIndex, dataIndex + dataSize),
);
}
}
if (tagData.length > 0 && tagData[tagData.length - 1] === '/') {
//selfClosing tag
if (tagAttributes) {
// <abc def="123"/>
tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1);
} else {
// <abc/>
tagName = tagName.substr(0, tagName.length - 1);
}
const childNode = new XMLNode(tagName, currentNode, '');
if (tagAttributes) {
childNode.attributes = parseAttributesString(
tagAttributes,
options,
);
}
currentNode.addChild(childNode);
} else {
//opening tag
const childNode = new XMLNode(tagName, currentNode);
if (
options.stopNodes.length &&
options.stopNodes.includes(childNode.tagName)
) {
childNode.startIndex = closeIndex;
}
if (tagAttributes && shouldBuildAttributesMap) {
childNode.attributes = parseAttributesString(
tagAttributes,
options,
);
}
currentNode.addChild(childNode);
currentNode = childNode;
}
i = closeIndex;
dataSize = 0;
dataIndex = i + 1;
}
} else {
dataSize++;
}
}
return traversable;
}
function concat(a, b) {
if (a === undefined) {
a = typeof b === 'string' ? '' : new Uint8Array(0);
}
if (b === undefined) {
b = typeof a === 'string' ? '' : new Uint8Array(0);
}
if (typeof a === 'string' && typeof b === 'string') {
return a + b;
} else if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
const arrayConcat = new Uint8Array(a.length + b.length);
arrayConcat.set(a);
arrayConcat.set(b, a.length);
return arrayConcat;
} else {
throw new Error(
`Unsuported value type for concatenation: ${typeof a} ${typeof b}`,
);
}
}
function removeNameSpaceIfNeeded(tagName, options) {
if (!options.ignoreNameSpace) return tagName;
const colonIndex = tagName.indexOf(':');
if (colonIndex !== -1) {
tagName = tagName.substr(colonIndex + 1);
}
}
/**
*
* @param {*} node
* @param {*} options
* @param {*} parentTagName
* @returns
*/
function traversableToJSON(node, options, parentTagName) {
const {
dynamicTypingNodeValue,
tagValueProcessor,
arrayMode,
tagNameProcessor,
attributeNameProcessor,
} = options;
const result = {};
if (tagValueProcessor) {
node.value = node.value && tagValueProcessor(node.value, node);
}
if (typeof node.value === 'string' && dynamicTypingNodeValue) {
node.value = dynamicTyping.parseString(node.value);
}
// when no child node or attr is present
if (
(!node.children || isEmptyObject(node.children)) &&
(!node.attributes || isEmptyObject(node.attributes))
) {
return node.value === undefined ? '' : node.value;
}
// otherwise create a textnode if node has some text
if (node.value !== undefined && node.value.length !== 0) {
const asArray = isTagNameInArrayMode(
node.tagName,
arrayMode,
parentTagName,
);
result[options.textNodeName] = asArray ? [node.value] : node.value;
}
if (node.attributes && !isEmptyObject(node.attributes)) {
let attributes = options.parseAttributesString ? {} : node.attributes;
if (options.attributeNamePrefix) {
// need to rename the attributes
const renamedAttributes = {};
for (let attributeName in node.attributes) {
const newAttributeName = attributeNameProcessor
? attributeNameProcessor(attributeName)
: attributeName;
renamedAttributes[options.attributeNamePrefix + newAttributeName] =
node.attributes[attributeName];
}
attributes = renamedAttributes;
}
if (options.attributesNodeName) {
let encapsulatedAttributes = {};
encapsulatedAttributes[options.attributesNodeName] = attributes;
attributes = encapsulatedAttributes;
}
merge(result, attributes, arrayMode);
}
for (const tagName in node.children) {
const newTagName = tagNameProcessor ? tagNameProcessor(tagName) : tagName;
if (node.children[tagName] && node.children[tagName].length > 1) {
result[tagName] = [];
for (let tag in node.children[tagName]) {
if (Object.prototype.hasOwnProperty.call(node.children[tagName], tag)) {
result[newTagName].push(
traversableToJSON(node.children[tagName][tag], options, tagName),
);
}
}
} else {
const subResult = traversableToJSON(
node.children[tagName][0],
options,
tagName,
);
const asArray =
(arrayMode === true && typeof subResult === 'object') ||
isTagNameInArrayMode(tagName, arrayMode, parentTagName);
result[newTagName] = asArray ? [subResult] : subResult;
}
}
return result;
}
/**
* Parse an ArrayBuffer or Uint8Array representing an XML
* @param {string|ArrayBuffer|Uint8Array} xmlData
* @param {object} [options={}]
* @param {string} [options.attributeNamePrefix='$']
* @param {boolean} [options.attributesNodeName=false]
* @param {string} [options.textNodeName='#text']
* @param {boolean} [options.trimValues=true] should we remove ascii < 32
* @param {boolean} [options.ignoreAttributes=false] skip attributes
* @param {boolean} [options.ignoreNameSpace=false]
* @param {boolean} [options.dynamicTypingAttributeValue=true] Parse attribute values that looks like number or boolean
* @param {boolean} [options.allowBooleanAttributes=false]
* @param {boolean} [options.dynamicTypingNodeValue=true] Parse tag values that looks like number or boolean
* @param {boolean} [options.arrayMode=false]
* @param {boolean} [options.cdataTagName=false]
* @param {function} [options.tagValueProcessor=(v, node) => decoder.decode(v)] Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder
* @param {function} [options.attributeValueProcessor=(v) => v] Attribute values can be modified during parsing
* @param {function} [options.tagNameProcessor=(v) => v] Callback allowing to rename tag names
* @param {function} [options.attributeNameProcessor=(v) => v] Callback allowing to rename attribute name
* @param {boolean} [options.stopNodes=[]] prevent further parsing
*
* @returns {object}
*/
function parse(xmlData, options = {}) {
if (typeof xmlData === 'string') {
const encoder = new TextEncoder();
xmlData = encoder.encode(xmlData);
}
if (!ArrayBuffer.isView(xmlData)) {
xmlData = new Uint8Array(xmlData);
}
options = { ...defaultOptions, ...options };
const traversable = getTraversable(xmlData, options);
return traversableToJSON(traversable, options);
}
exports.parse = parse;
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./parse"), exports);
//# sourceMappingURL=index.js.map
{
"name": "arraybuffer-xml-parser",
"version": "0.5.0",
"version": "0.5.1",
"description": "Parse XML files contained in an array buffer",
"main": "lib/index.js",
"module": "src/index.js",
"main": "./lib/index.js",
"module": "./lib-esm/index.js",
"types": "./lib/index.d.ts",
"files": [
"src",
"lib",
"src"
"lib-esm"
],
"scripts": {
"build": "cheminfo-build --entry src/index.js --root XMLParser",
"build": "npm run tsc-esm && cheminfo-build --entry lib-esm/index.js --root XMLParser",
"check-types": "tsc --noEmit",
"clean": "rimraf lib lib-esm",
"eslint": "eslint src",
"eslint-fix": "npm run eslint -- --fix",
"prepack": "rollup -c",
"prepack": "npm run tsc",
"prettier": "prettier --check src",
"prettier-write": "prettier --write src",
"test": "npm run test-coverage && npm run eslint && npm run prettier",
"test": "npm run test-coverage && npm run eslint && npm run prettier && npm run check-types",
"test-coverage": "jest --coverage",
"test-only": "jest"
"test-only": "jest",
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm",
"tsc-cjs": "tsc --project tsconfig.cjs.json",
"tsc-esm": "tsc --project tsconfig.esm.json"
},

@@ -33,17 +40,16 @@ "repository": {

"homepage": "https://github.com/cheminfo/arraybuffer-xml-parser#readme",
"jest": {
"testEnvironment": "node"
},
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.15.4",
"@types/jest": "^27.0.2",
"@types/he": "^1.1.2",
"@types/jest": "^27.5.0",
"cheminfo-build": "^1.1.11",
"eslint": "^8.0.1",
"eslint-config-cheminfo": "^7.1.1",
"eslint": "^8.15.0",
"eslint-config-cheminfo-typescript": "^10.4.0",
"he": "^1.2.0",
"iobuffer": "^5.0.4",
"jest": "^27.3.1",
"iobuffer": "^5.1.0",
"jest": "^28.1.0",
"pako": "^2.0.4",
"prettier": "^2.4.1",
"rollup": "^2.58.0",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"ts-jest": "^28.0.2",
"typescript": "^4.6.4",
"uint8-base64": "^0.1.1"

@@ -50,0 +56,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc