npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@endevops/parser

v0.0.1

Published

XML parser in pure JavaScript with pluggable output builders and composable value parsers. Fork of @nodable/flexible-xml-parser, with static types across the source tree and a fully typed test suite.

Readme

@endevops/parser

A fork of @nodable/flexible-xml-parser, a high-performance XML parser in pure JavaScript with pluggable output builders, composable value parsers, and string, buffer, stream, and incremental feed input modes.

This is a fork

This project is not the original parser. It started as a copy of @nodable/flexible-xml-parser at commit f51ecad5 and is maintained separately by Endevops. The package name changed and the code is edited, so this repository is the place to file issues against the fork, not the upstream one.

Upstream released @nodable/flexible-xml-parser as the scoped successor to the unscoped fast-xml-parser, so the credit chain runs fast-xml-parser (Amit Gupta) to @nodable/flexible-xml-parser to this fork. The flexible-xml-parser name in this package name is inherited from upstream, not chosen here.

What this fork changes

The parsing behaviour is the same. The changes are in how the code is written, built, and called.

| Change | Why | | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | Every file and directory renamed to dash-case | The upstream names were PascalCase and SCREAMING_CASE in the same tree | | Static types across all of src/ | Upstream shipped types only on the public entry points | | Every entry point returns an Effect | Construction and parsing can fail, so they report failure on a typed channel instead of throwing | | One error class per parse failure | A limit, a broken tag and a refused option are three different decisions, so each gets a class | | Test suite and benchmark fully typed | The specs are now checked by the compiler, which surfaced real bugs | | Built with Vite+ (vp pack, vp test, vp check) | Replaces the previous ad-hoc build setup | | Latent bugs fixed in specs and entity handling | Found while typing, listed in the commit history | | Path matching and name validation are workspace pkgs | The path-expression-matcher type augmentations are gone; see below |

Two known differences worth calling out: test/compact-builder-force.spec.ts is fork-local.

bench/parse.bench.ts is a Vitest benchmark comparing whole-shot parse() against chunked feed()/end(). Run it with vp run bench from the workspace root, or vp test bench packages/parser for this package alone. vp test skips it.

The path-expression-matcher augmentations are gone

The parser used to carry src/path-expression-matcher.d.ts, a module augmentation patching three things the published index.d.ts got wrong: the third data constructor argument it never declared, findMatch()'s non-nullable return, and the stale doc comments. It also exported a ConfigurableExpressionCtor alias, because the two-argument declaration made the parser's three-argument construction a TS2554 at every call site.

All four are now real. Expression and ExpressionSet — which ship in @endevops/common-xml — are generic over the expression payload, so ExpressionSet<TagExpressionConfig> carries the config type from construction through to findMatch().data with no cast and no augmentation, which is what let the file be deleted rather than trimmed.

The output builders are in the workspace too, as @endevops/builder. Upstream @nodable/base-output-builder and @nodable/compact-builder ship declarations that name the upstream path-expression-matcher from npm, so a pnpm overrides entry used to point that transitive dependency at the workspace package — one copy of the types rather than two structurally-identical-but-distinct ones, without which every builder factory failed to satisfy the parser's structural contract. Merging the builders into this workspace removed the need for that override, and with it the remaining src/nodable-builders.d.ts augmentations.

Installation

The package is not published to npm. Clone the repository and link it into a consuming project, or point a pnpm catalog: entry at the local path.

pnpm install
pnpm build

Its runtime dependencies are effect and the two workspace packages @endevops/common-xml and @endevops/builder.

Quick start

Every entry point returns an Effect, so these examples run one to get a value back. In real code prefer Effect.runPromise or a runtime.

import { Effect } from 'effect';
import XMLParser from '@endevops/parser';

const parser = Effect.runSync(XMLParser.make());
Effect.runSync(parser.parse('<root><count>3</count><active>true</active></root>'));
// { root: { count: 3, active: true } }

Attributes are skipped by default. Turn them on to see them:

const parser = Effect.runSync(XMLParser.make({ skip: { attributes: false } }));
Effect.runSync(parser.parse('<item id="1">hello</item>'));
// { item: { '@_id': 1, '#text': 'hello' } }

A ParseError — a union of 33 classes, one per cause, each tagged with its ErrorCode value, and each carrying the numbers and names a handler needs — is the only thing that can fail. Recover from a code you can handle with Effect.catchTag; a code you do not handle re-fails unchanged, and the handler is handed that class rather than a payload in a wrapper.

Input modes

const parser = Effect.runSync(XMLParser.make());

Effect.runSync(parser.parse('<root/>')); // string
Effect.runSync(parser.parse(Buffer.from('<root/>'))); // buffer
Effect.runSync(parser.parseBytesArr(new Uint8Array([...]))); // typed array
await Effect.runPromise(parser.parseStream(fs.createReadStream('big.xml'))); // Node.js readable

// Incremental feed — feed() yields the parser back, end() produces the result
const streamed = Effect.runSync(XMLParser.make());
Effect.runSync(streamed.feed('<root>'));
Effect.runSync(streamed.feed('<item>1</item>'));
Effect.runSync(streamed.feed('</root>'));
const result = Effect.runSync(streamed.end());
// { root: { item: 1 } }

Options

Everything is optional. XMLParser.make resolves and validates them, so this object literal is its argument:

XMLParser.make({
  skip: {
    // What to leave out of the output
    attributes: true, // Skip all attributes
    declaration: false, // Skip <?xml ...?>
    pi: false, // Skip processing instructions
    cdata: false, // Leave CDATA out of the output
    comment: false, // Leave comments out of the output
    nsPrefix: false, // Strip namespace prefixes
    tags: [], // Tag paths to drop from the output
  },
  nameFor: {
    // Property names for special nodes
    text: '#text', // Mixed-content text property
    cdata: '', // '' merges into text, '#cdata' gets its own key
    comment: '', // '' omits, '#comment' captures
  },
  attributes: {
    // Attribute representation
    prefix: '@_',
    suffix: '',
    groupBy: '', // Group attributes under one key, '' keeps them inline
    booleanType: 'allow', // 'allow' reads valueless attributes as true, 'ignore' drops them, 'throw' rejects
  },
  tags: {
    unpaired: [], // Self-closing tags written without a slash
    stopNodes: [], // Paths whose content is captured raw
  },
  limits: { maxNestedTags: null, maxAttributesPerTag: null },
  doctypeOptions: { enabled: false, maxEntityCount: 100, maxEntitySize: 10000 },
  strictReservedNames: false,
  exitIf: null,
  feedable: { maxBufferSize: 10 * 1024 * 1024, autoFlush: true, flushThreshold: 1024 },
  autoClose: null, // null is strict, 'html' recovers and collects errors
  // OutputBuilder is omitted, not set to null — omit it to get CompactBuilder
});

Value parsers

Value parsing belongs to the output builder, so tag text and attribute values get independent chains.

import { Effect } from 'effect';
import { CompactBuilderFactory } from '@endevops/builder';

const builder = Effect.runSync(
  CompactBuilderFactory.make({
    tags: { valueParsers: ['entity', 'boolean', 'number'] },
    attributes: { valueParsers: ['entity', 'number', 'boolean'] },
  })
);

const parser = Effect.runSync(XMLParser.make({ OutputBuilder: builder }));

Documentation

The docs are inherited from upstream. Their install and import snippets name this package; the option reference and the internals notes still describe upstream behaviour in upstream's terms, so check a snippet against 10 — TypeScript if it disagrees with your editor.

| File | Topic | | -------------------------------------------------------------- | ------------------------------------------------ | | docs/01-getting-started.md | Installation, first parse, common patterns | | docs/02-options.md | Full options reference | | docs/03-value-parsers.md | Value parser pipeline, built-ins, custom parsers | | docs/04-stop-nodes.md | Stop nodes and skip tags | | docs/05-output-builders.md | Built-in and custom output builders | | docs/06-streaming.md | Stream, feed and end, memory behaviour | | docs/07-auto-close.md | Lenient HTML parsing and error collection | | docs/08-security.md | DoS limits and prototype pollution | | docs/09-path-expressions.md | Path expression syntax | | docs/10-typescript.md | TypeScript usage and type definitions | | docs/16-encoding.md | Encoding detection and decoding |

Thanks

This parser exists because Amit Gupta wrote fast-xml-parser and then @nodable/flexible-xml-parser. The tag scanning, attribute handling, value coercion, stop nodes, streaming design, and the output builder split that makes this parser configurable are all his work. The MIT license he chose for both packages is what makes this fork possible.

Thanks also to everyone who has reported a bug, sent a pull request, or answered an issue on either repository. A fork only stays useful when the original keeps moving, and that is mostly thanks to the people who keep sending it fixes.

This fork exists because of that work, and the same MIT terms apply to it.

License

MIT, the same as upstream. See LICENSE for the full text. The copyright notices for Amit Gupta (2026, @nodable/flexible-xml-parser) and Amit Kumar Gupta (2017, fast-xml-parser) are retained there alongside the fork's own, as the MIT terms require.