///
declare module "bs-raw" {
interface TextDecoder {
decode(input?: any): string;
}
interface TextEncoder {
encode(input?: string): Uint8Array;
}
/**
* Tiny buffer class for UTF8 strings.
* @internal
*
* It is a subclass of Uint8Array with the added capacity to encode/decode text from/into string and therefore requires to be initialised with BS.setupEncodeDecoder() as such:
*
* ```javascript
* // Encode & Decode using Buffer class (nodejs)
* BS.setupEncodeDecoder(
* {
* encode(s) {
* return BS.create(Buffer.from(s || ''))
* },
* },
* {
* decode(bs) {
* return Buffer.from(bs).toString()
* },
* }
* )
* ```
*
* or
*
* ```javascript
* // Encode & Decode using TextEncoder and TextDecoder class (nodejs)
* // import { TextEncoder, TextDecoder } from 'util' // required in nodejs
* BS.setupEncodeDecoder(new TextEncoder(), new TextDecoder())
* ```
*
* @remarks
*
* Use `BS.create()` to create an instance.
* `slice()` and `subarray()` will return a BS instance.
*
*/
export class BS extends Uint8Array {
/**
* Test whether this byte string is the start of another one for a given length
*
* const abc = BS.create('abc')
* const abcdef = BS.create('abcdef')
* abc.isStartOf(abcdef, 3) === true
* abc.isStartOf(abcdef, 4) === false
*/
isStartOf(bs: Uint8Array, length: number): boolean;
/**
* Test strick equality
*
* const abc1 = BS.create('abc')
* const abc2 = BS.create('abc')
* abc1 !== abc2
* abc1.equals(abc2) === true
*/
equals(bs: Uint8Array): boolean;
/** creates an istance from a string or a Uint8Array */
static create: (input: string | Uint8Array) => BS;
/**
* Extend a byte string with another one
*
* const abc = BS.create('abc')
* const def = BS.create('def')
* const abcdef = abc.append(def)
* abcdef.toString() === 'abcdef'
*/
append(tail: BS): BS;
/** converts to string */
toString(start?: number, length?: number): string;
/** @internal */
static encode(_input?: string): BS;
/** @internal */
static decode(_input?: BS): string;
/** Defines encoder and decoder - must be called before use */
static setupEncodeDecoder(textEncoder: TextEncoder, textDecoder: TextDecoder): void;
/** @internal peeks the first few chars for debugging */
get _(): string;
/** @internal singleton reffering to an empty array (used for initialisation of internal variables) */
static get EMPTY(): BS;
}
/** @internal */
export interface BS {
slice(start?: number, end?: number): BS;
subarray(start?: number, end?: number): BS;
}
}
declare module "bs-browser" {
/** Setting up text decoder for node */
import { BS } from "bs-raw";
export { BS };
}
declare module "bs" {
import { BS } from "bs-raw";
/** @public */
export { BS };
}
declare module "token" {
import { BS } from "bs";
export type Attribute = {
name: BS;
value: BS;
};
export type Ns = {
name?: BS;
uriString: string;
uri: BS;
};
/**
* Defininition of the tokens emmited by the parser
* @public
*/
export namespace Token {
/** @internal */
type Token = Token.EndTag | Token.StartTag | Token.Text | Token.CDATA | Token.Comment;
/**
* Represent a start-tag, ie, ``
* @public
*/
class StartTag {
readonly name: BS;
constructor(name: BS, atts: Attribute[], ns: Ns[], selfclosing?: true);
/** @internal */
readonly atts?: {
name: BS;
value: BS;
}[];
/** @internal */
readonly ns?: Ns[];
/** self-closing start-tag, like `` */
readonly selfClosing?: true;
/**
* namespace URI
*
* with ``, aStartTag.namespaceUri === '//uri/x'
*/
get namespaceUri(): string | undefined;
/**
* with ``, aStartTag.tagName === 'x:a'
*/
get tagName(): string;
/**
* with ``, aStartTag.localName === 'a'
*/
get localName(): string;
/**
* with ``, aStartTag.getAttribute('att') === 'value'
*/
getAttribute(name: string): string | undefined;
/** @internal */
getAttributeFQN(fqName: BS): string | undefined;
/**
* with ``, aStartTag.getAttributeNS('//uri/x', 'att') === 'value'
*/
getAttributeNS(nsUri: string, localName: string): string | undefined;
/** @internal */
get length(): number;
/** @internal */
get bs(): BS;
/** return this tag as a string (extra space-like character ommited) */
toString(): string;
}
/**
* Represent a CDATA node, ie, ``
* @public
*/
class CDATA {
readonly content: BS;
constructor(content: BS);
/** return text as a string */
get textContent(): string;
/** return this tag as a string */
toString(): string;
}
/**
* Represent a Text node
* @public
*/
class Text {
readonly content: BS;
constructor(content: BS);
/** return text as a string, decoding xml entities */
toString(): string;
/** return text as a string */
get textContent(): string;
private decode;
/** Supported entities (in addition to hex and dec codepoints)
*
* @remarks
*
* Default entities: `&`, `>`, `<`, `"` and `'`
*/
static entities: {
c: string;
bs: number[];
}[];
}
/**
* Represent a comment node, ie, ``
* @public
*/
class Comment {
readonly content: BS;
constructor(content: BS);
toString(): string;
/** return comment as a string */
get textContent(): string;
}
/**
* Represent a end-tag, ie ``
* @public
*/
class EndTag {
readonly name: BS;
constructor(name: BS);
toString(): string;
}
}
}
declare module "ns-resolver" {
import { BS } from "bs";
type NsEntry = {
srcName: BS | undefined;
trgName: BS | undefined;
uri: BS;
uriString: string;
};
export type NsResolverMap = {
[nsName: string]: string;
};
/**
* Namespace resolver
*
* Representing the mapping of epxected namespaces and their URI's
*
* Created using an object whith namespaces as keys and URI's as values
*
* const nsResolver = NsResolver.create({svg: 'http://www.w3.org/2000/svg'})
*/
export class NsResolver extends Array {
/** creates an instance of `NsResolver` */
static create(nsResolverMap: NsResolverMap): NsResolver;
/**
* create a new resolver inheriting this one with possibly additional namespace mappings of the current XML files
*
* const nsResolver = NsResolver.create({
* abc: 'uri://abc',
* lmn: 'uri://lmn',
* '': 'uri://uvw',
* })
* const nsResolverChild = nsResolver.forChildTag([
* { name: BS.create('abd'), uri: BS.create('uri://abc') },
* { name: undefined, uri: BS.create('uri://lmn') },
* { name: BS.create('uvw'), uri: BS.create('uri://uvw') },
* ])
* nsResolverChild.resolveTagName(BS.create('abd:tagName1')) === 'abc:tagName1'
* nsResolverChild.resolveTagName(BS.create('tagName2')) === 'lmn:tagName2'
* nsResolverChild.resolveTagName(BS.create('uvw:tagName3')) === 'tagName3'
*
*/
forChildTag(ns: {
name?: BS;
uri: BS;
}[]): NsResolver;
/** resolve the namespace of a fully qualified tag name */
resolveTagName(name: BS): BS;
private replaceNs;
/** resolve the namespace of a fully qualified attribute name */
resolveAtts(atts: {
name: BS;
value: BS;
}[]): {
name: BS;
value: BS;
}[];
/** resolve the namespace set (attributes with 'xmlns' prefix) of a tag */
resolveNs(tagNs: {
name?: BS;
uri: BS;
}[]): {
name: BS | undefined;
uriString: string;
uri: BS;
}[];
}
}
declare module "tokenizer" {
import { BS } from "bs";
import { Token } from "token";
import { NsResolverMap } from "ns-resolver";
/**
* Produces tokens while scanning the input stream.
* @public
*
* @remarks
*
* Will emit (using `nextToken()`) any of StartTag, EndTag, Text, Comment, CDATA or undefined when the stream chunk is exhauseted (the last full tag was scanned).
* On the next write, scanning resumes taking into account any uncomplete tag from the previous chunk.
*
* @example
* Writing the xml in 2 chunks:
*
* ```javascript
* const tokenizer = new Tokenizer()
* tokenizer.write('someinn')
* while ((token = tokenizer.nextToken())) {
* // do something
* }
* tokenizer.write('ertext')
* while ((token = tokenizer.nextToken())) {
* // do some more
* }
* ```
*
*/
export class Tokenizer {
private at;
private bs;
/** carry over partial names or content to next chunk */
private tail;
/** tag name of pending start or end tag */
private name;
/** name of pending attribute */
private attName;
/** list of pending attributes */
private atts;
/** list of pending name spaces */
private ns;
/** state of tokenizer of previous chunk */
private state;
private seenLength;
/** namespace resolver */
private nsResolver?;
private nsResolverStack;
private parentNames;
private skippingLevel;
private skippingEndTag;
constructor(nsResolverMap?: NsResolverMap);
/**
* Write some bytes to be scanned.
*
* @param bs - byte sequence as an instance of `BS`, `Buffer` or `string`
*/
write(bs: BS | string | Buffer | Uint8Array): void;
get exhausted(): boolean;
/**
* Gets the next token
*
* return `undefined` when fully scanned or last tag was incomplete, waiting for more.
*/
nextToken(): Token.EndTag | Token.StartTag | Token.Text | Token.CDATA | Token.Comment | undefined;
/**
* When called, the tokenizer will skip decoding the child nodes of the last tag.
* But scanning will resume on its end-tag. This end-tag will not be emitted when called with `true` (`tokenizer.skipChildNodes(true)`)
* This is a performance optimization as there's no need to decode text, attribute values...
*
* @example
* Skipping nodes
*
* ```javascript
* tokenizer.write('someinnertext')
* const aStart = tokenizer.nextToken()
* const bStart = tokenizer.nextToken()
* bStart.getAttribute('att') === 'value'
* tokenizer.skipChildNodes()
* const bEnd = tokenizer.nextToken()
* bEnd.toString() === ''
* ```
*/
skipChildNodes(skippingEndTag?: boolean): void;
/** Capuring a name */
private getName;
/** Capuring a sequence */
private capture;
/** Capuring content ending with a given byte */
private startingContentCapture;
/** detecting the possible end of content */
private mayBeEndingContentCapture;
/** detecting the actual end of content */
private hasContentCaputreEnded;
/** returns the catpure (value) */
private flushCapture;
/** returns the content of comment or CDATA */
private flushCaptureContent;
/** if skipping this token */
private get isSkipping();
/** creates a StartTag */
private startTag;
/** creates an EndTag */
private endTag;
/** skipping spaces, ie after names */
private skipSpaceLike;
/** testing 'XMLNS' or 'xmlns' sequence */
private isAtXmlns;
/** testing 'xmlns' sequence */
private isAt_xmlns;
/** testing 'XMLNS' sequence */
private isAt_XMLNS;
}
}
declare module "parser/xml-like" {
import { Token } from "token";
import { Parser } from "parser/index";
/**
* Representation of an xml element
* @beta
*
*/
export class XmlElement {
readonly startTag: Token.StartTag;
constructor(startTag: Token.StartTag);
/**
* {@inheritDoc Token.StartTag.getAttribute}
*/
getAttribute(name: string): string | undefined;
/** see {@link Token.StartTag.tagName} */
get tagName(): string;
/** Children */
children: (XmlElement | Token.Text | Token.CDATA)[];
/** string as xml */
toString(): string;
}
/**
* Parser producing a representation of an xml element see class `XmlElement`
* @beta
*/
export const XmlElementParser: () => Parser.IParser;
}
declare module "parser/to-object" {
import { Parser } from "parser/index";
import { Token } from "token";
export const createObject: (startTag: Token.StartTag) => any;
/**
* Simplified, opiniated, representation of an xml element
*
* Produce an object where attributes are properties (string) of the object and
* children element are arrays of objects.
*
* @remarks
* - Fully qualified names are used for property names.
* - All texts are collected in property `#text`
*/
export class XmlToObjectParser implements Parser.IParser {
onStart(startTag: Token.StartTag, parentCtx: any): void;
onText(text: Token.Text | Token.CDATA, ctx: any): void;
onChild(): this;
}
}
declare module "parser/index" {
import { Token } from "token";
import { BS } from "bs";
import * as XmlLike from "parser/xml-like";
/**
* Parsing XML Element
* @public
*/
export namespace Parser {
/**
* Interface that must be implemented by a parser
* @public
*/
interface IParser {
/**
* Called on start-tag of a child element.
*
* Receiving the start-tag opened and a parser if different than the parent parser. If returns `false`, the child element is skipped.
*/
onChild?: (startTag: Token.StartTag) => IParser | false;
/**
* Called on end-tag.
*
* Receiving the context of the current element being closed and the context of its parent
*/
onEnd?: (ctx: any, parentCtx: any) => any;
/**
* Called on start-tag.
*
* Receiving the context of the current element being opened and the context of its parent
*/
onStart?: (startTag: Token.StartTag, parentCtx: any) => any;
/**
* Called on text or CDATA.
*
* Receiving the text token (`text.textContent`) and the context of its parent
*/
onText?: (text: Token.Text | Token.CDATA, ctx: any) => void;
/** when `true`, child nodes are ignored */
skipChildNodes?: boolean;
/** @internal */
passThrough?: boolean;
}
/**
* Helps with intermediate nodes of a path like `a/b/c`.
*
* When calling docParser.on(`a/b/c`, myParser), `c` will be associated with the target parser but `a` and `b` will be associatiated with `PassThrough`
*/
class PassThrough implements IParser {
readonly names: BS[];
readonly parser: IParser;
passThrough: boolean;
private constructor();
static on(path: string, parser: IParser): PassThrough;
onChild(startTag: Token.StartTag): false | IParser | PassThrough;
}
/**
* Switches to the first parser amongst a list of possible parsers
*/
class Switch implements IParser {
readonly then: IParser[];
private constructor();
static create(): Switch;
/**
* Adds a parser to the list
* @param parser-parser to be added
*/
case(parser: IParser): this;
/**
* Returns the first parser matching the start tag
* @param startTag-current start-tag
*/
onChild(startTag: Token.StartTag): false | IParser | Switch;
}
/**
* Returns parser to generate a simple object representation of an xml element
*
* @beta
*/
function getXmlToObjectParser(): IParser;
/** Representation of an xml element */
export import XmlElement = XmlLike.XmlElement;
/** Returns a object representation based on XmlElement */
export import XmlElementParser = XmlLike.XmlElementParser;
}
}
declare module "index" {
/**
* @packageDocumentation
* A simple js parser for XML for nodejs and the browser.
*
* It will handle smoothly line-breaks, spaces, chunks (from stream), namespaces...
*
* It is fast and optimized for Buffer or Uint8Array of encoded strings (utf8) but will take strings too.
*/
export { Tokenizer } from "tokenizer";
export { DocumentParser } from "document-parser";
export { Parser } from "parser/index";
export { Token } from "token";
}
declare module "document-parser" {
import { Tokenizer } from "index";
import { BS } from "bs";
import { Parser } from "parser/index";
/**
* Defines a document parser.
* @public
*/
export class DocumentParser {
private tokenizer;
private openElement?;
constructor(tokenizer: Tokenizer);
/**
* Defines how a set of nodes should be parsed
* @public
* @param path - a node name of a path to a node (similar to file path, this is not a xpath)
* @param parser - a parser to be applied
*/
on(path: string, parser: Parser.IParser): Parser.IParser;
/**
* Defines, as for `on()` how the whole document should be parsed
* @param parser - parser to be applied
*/
onRoot(parser: Parser.IParser): void;
/**
* Writes xml chunks to the parser
* @remarks
* Before writing a new chunk the current one should be exhausted (all tokens have been processed, this means {@link DocumentParser.next} has returned `undefined`)
* @param chunk- byte sequence (utf8)
*/
write(chunk: BS | string | Buffer | Uint8Array): void;
/**
* Getting the next element
*/
next(): any;
private resolveTag;
}
}