xml-event-parser 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,80 +1,122 @@
1
1
  xml-event-parser
2
2
  ================
3
3
 
4
- A tiny XML parser with a document tree and path-based events, produced in a
5
- single pass.
6
-
7
- - **Zero dependencies**, no DOM, no `node:*` imports, no `eval`.
8
- - **One pass, two views**: every element is available as a tree node and as an
9
- `open`/`close` event keyed by its dotted path, so a consumer can convert while
10
- parsing without a second traversal.
11
- - **Streaming**: feed `write(chunk)` from a file or socket and call `end()`.
12
- Chunks may split anywhere - mid-tag, mid-attribute, inside CDATA or a comment -
13
- and `retain` decides whether the parser keeps the tree, one root, or nothing,
14
- so a 60 MiB document can be parsed with ~0 MiB of retained heap.
15
- - **Small**: 6.1 KiB minified, 2.1 KiB gzipped (ESM), streaming included.
16
- - **Fastest on every shape we benchmarked**: ~1.9x `txml`'s throughput on
17
- OSM-style documents at ~30% less retained heap, and ahead of `txml` on
18
- text/entity-heavy input too; ~1.6x `htmlparser2`'s on element callbacks. See
19
- [Benchmarks](#benchmarks).
4
+ **Stream XML without loading it.** A tiny parser that gives you a document tree
5
+ and path-based events in one pass.
20
6
 
21
- ```js
22
- import { XmlParser } from 'xml-event-parser';
7
+ - **Bounded memory**: keep only the element you are reading. A 240 MB report
8
+ parses in 81 MiB of RSS.
9
+ - **Fast**: ~3.7x `sax` and ~4x `fast-xml-parser` on the same report.
10
+ - **Portable**: 6.4 KiB minified / 2.2 KiB gzipped, zero dependencies, no
11
+ `node:*` imports in the core entry, no `eval`.
12
+ - **One pass, two views**: a tree node and an `open`/`close` event per element.
23
13
 
24
- const parser = new XmlParser({ progressive: true });
14
+ ## When to use this
25
15
 
26
- parser.on('</osm.way>', (way) => {
27
- // way.tagName === 'way', way.id, way.version, way.children ...
28
- console.log(way.id, way.children?.length ?? 0);
29
- });
16
+ | your job | use |
17
+ | --- | --- |
18
+ | Read a file bigger than memory: CI/test reports (JUnit, SARIF, coverage), sitemaps, GPX/KML tracks, OSM extracts | **this**, `xml-event-parser/stream` |
19
+ | Pick elements while streaming without writing a SAX state machine | **this**, path events |
20
+ | Parse occasionally in a browser or Worker where a 20 KiB parser is too much | **this**, core entry |
21
+ | Turn a small config XML into a plain object | `fast-xml-parser` or `xml2js` |
22
+ | DOM semantics: namespaces, XPath, mutation | `@xmldom/xmldom` |
23
+ | Hand-tuned callbacks with no tree at all | `sax` |
30
24
 
31
- const roots = parser.parse(xml);
32
- ```
25
+ ## 240 MB in one second
33
26
 
34
- ## Node shape
27
+ Generated JUnit reports, each parser in its own process capped at 512 MB of heap:
35
28
 
36
- ```ts
37
- interface XmlNode {
38
- tagName: string; // element name, e.g. 'way'
39
- children?: XmlNode[]; // present only when the element has child elements
40
- text?: string; // present only for text-only elements
41
- [attribute: string]: any; // attributes, copied flat onto the node
42
- }
29
+ | parser | 60 MB report | 240 MB report |
30
+ | --- | --- | --- |
31
+ | **xml-event-parser** | **0.27 s, 65 MiB** | **1.03 s, 81 MiB** |
32
+ | sax | 0.96 s, 67 MiB | 3.80 s, 79 MiB |
33
+ | fast-xml-parser | 1.52 s, 637 MiB | **out of memory** |
34
+
35
+ The file grows 4x; the heap does not.
36
+
37
+ ## Install
38
+
39
+ ```sh
40
+ npm install xml-event-parser
43
41
  ```
44
42
 
45
- Attributes are flat rather than nested in an `attributes` object on purpose: one
46
- object per element keeps a parsed document small (see the heap column in
47
- [`bench/RESULTS.md`](bench/RESULTS.md)). Names that collide with `tagName`,
48
- `children` or `text` are shadowed by the element's own values; if that matters
49
- for your vocabulary, read attributes off the source XML instead.
43
+ ## Quick start
50
44
 
51
- An element with nested tags is emitted on close **after** its children are
52
- attached, so `node.children` is complete inside a close listener.
45
+ ### Tree
53
46
 
54
- ## Streaming
47
+ ```js
48
+ import { XmlParser } from 'xml-event-parser';
49
+
50
+ const roots = new XmlParser().parse(xml);
51
+ console.log(roots[0].tagName, roots[0].children);
52
+ ```
53
+
54
+ ### Stream (Node)
55
55
 
56
56
  ```js
57
57
  import { createReadStream } from 'node:fs';
58
- import { XmlParser } from 'xml-event-parser';
58
+ import { Writable } from 'node:stream';
59
+ import { pipeline } from 'node:stream/promises';
60
+ import { createStream } from 'xml-event-parser/stream';
61
+
62
+ await pipeline(
63
+ createReadStream('junit.xml'),
64
+ createStream({ event: '</testsuites.testsuite.testcase>' }),
65
+ new Writable({
66
+ objectMode: true,
67
+ write(testcase, _encoding, done) {
68
+ // testcase is complete: attributes plus <failure>/<system-out> children
69
+ if (testcase.children?.some((child) => child.tagName === 'failure')) {
70
+ console.log(testcase.name);
71
+ }
72
+ done();
73
+ },
74
+ }),
75
+ );
76
+ ```
59
77
 
60
- const parser = new XmlParser({ progressive: true, retain: 'none' });
61
- parser.on('</osm.way>', (way) => {
62
- // one complete element; `way` is released after this returns
78
+ ### Anywhere (no Node built-ins)
79
+
80
+ ```js
81
+ const parser = new XmlParser({
82
+ progressive: true,
83
+ retain: 'none',
84
+ keep: ['testsuites.testsuite.testcase'],
63
85
  });
64
86
 
65
- for await (const chunk of createReadStream('planet.osm.xml', 'utf8')) {
87
+ parser.on('</testsuites.testsuite.testcase>', (testcase) => { /* ... */ });
88
+
89
+ for await (const chunk of stream) {
66
90
  parser.write(chunk);
67
91
  }
68
92
  parser.end();
69
93
  ```
70
94
 
71
- `write()` consumes everything complete in the buffer and keeps the rest for the
72
- next call, so a chunk boundary can fall inside a tag, an attribute value, a text
73
- run, a comment or a `]]>` terminator without changing the result: parsing a
74
- document whole or in 1-byte chunks produces the same tree (the suite checks every
75
- split point).
95
+ Runnable examples: [`examples/junit.mjs`](examples/junit.mjs),
96
+ [`examples/sitemap.mjs`](examples/sitemap.mjs),
97
+ [`examples/gpx.mjs`](examples/gpx.mjs).
98
+
99
+ ## API
100
+
101
+ ### Node shape
102
+
103
+ ```ts
104
+ interface XmlNode {
105
+ tagName: string; // element name
106
+ children?: XmlNode[]; // present only when the element has child elements
107
+ text?: string; // present only for text-only elements
108
+ [attribute: string]: any; // attributes, flat on the node
109
+ }
110
+ ```
111
+
112
+ An element is emitted on close after its children are attached, so
113
+ `node.children` is complete inside a close listener. Attributes whose names
114
+ collide with `tagName`, `children` or `text` are shadowed by those fields.
76
115
 
77
- `retain` is what makes streaming bounded:
116
+ ### Streaming and retention
117
+
118
+ `write(chunk)` accepts a document in pieces; chunks may split anywhere, and
119
+ `end()` closes whatever is still open and returns the roots.
78
120
 
79
121
  | `retain` | keeps | `end()` returns | memory |
80
122
  | --- | --- | --- | --- |
@@ -82,142 +124,70 @@ split point).
82
124
  | `'root'` | the root being built | `[]`; a finished root is emitted and dropped | largest root |
83
125
  | `'none'` | nothing | `[]`; every element is emitted and dropped | nesting depth |
84
126
 
85
- Measured on a 60 MiB document (ten concatenated extracts, 327k elements):
86
- `retain: 'all'` holds 302 MiB, `retain: 'none'` holds ~0 MiB, both emit the same
87
- 327k elements. Without listeners, `retain: 'none'` still parses - it just throws
88
- the result away.
127
+ With `retain: 'none'`, `keep` restores the subtree of the elements you want, so
128
+ a streamed element arrives complete rather than as a bare tag:
129
+
130
+ ```js
131
+ new XmlParser({
132
+ progressive: true,
133
+ retain: 'none',
134
+ keep: ['testsuites.testsuite.testcase'],
135
+ });
136
+ ```
137
+
138
+ `createStream` applies `keep` for you from its `event` path.
89
139
 
90
- ## Events
140
+ ### Events
91
141
 
92
142
  | form | fires |
93
143
  | --- | --- |
94
144
  | `'<osm.way>'` | when a `<way>` inside `<osm>` opens |
95
145
  | `'</osm.way>'` | when it closes, with `children` populated |
96
146
 
97
- The path is the dotted chain of ancestors. Event names are only built for paths
98
- that have a listener, so subscribing to one path costs nothing for the elements
99
- you ignore. Filter inside the listener:
147
+ The path is the dotted chain of ancestors. Filter inside the listener:
100
148
 
101
149
  ```js
102
150
  parser.on('</osm.way>', (way) => {
103
- if (!way.tags) return; // or any other predicate
151
+ if (!way.tags) return;
104
152
  });
105
153
  ```
106
154
 
107
- ## Options
155
+ ### Options
108
156
 
109
157
  | option | default | meaning |
110
158
  | --- | --- | --- |
111
- | `progressive` | `false` | emit events. Without it, `parse()` only builds the tree |
112
- | `queryParent` | `false` | track parents so `parser.getParent(node)` works (costs a `WeakMap`) |
113
- | `retain` | `'all'` | what survives an element: `'all'`, `'root'` or `'none'` (see [Streaming](#streaming)) |
159
+ | `progressive` | `false` | emit events; without it `parse()` only builds the tree |
160
+ | `queryParent` | `false` | track parents for `parser.getParent(node)` |
161
+ | `retain` | `'all'` | `'all'`, `'root'` or `'none'` (see [Streaming and retention](#streaming-and-retention)) |
162
+ | `keep` | `[]` | under `retain: 'none'`, paths whose elements keep their subtree |
114
163
 
115
- `parse(xml, parent?, dir?)` also accepts an existing node to attach the parsed
116
- roots to, and a dotted path prefix used when building event names.
164
+ `parse(xml, parent?, dir?)` can attach the parsed roots to an existing node and
165
+ prefix event paths.
117
166
 
118
- The parser is non-validating and never throws on malformed input: unterminated
119
- tags, comments, processing instructions and doctypes are skipped, and elements
120
- still open at end of input are closed so a truncated download yields the
121
- document parsed so far (`'<r><a>text'` returns `r` with an `a` child whose
122
- `text` is `'text'`).
123
-
124
- CDATA sections are content, not markup: their text is kept verbatim and is not
125
- entity-decoded, while text outside them is decoded (the five predefined
126
- entities and numeric character references).
167
+ Malformed input never throws: comments, processing instructions and doctypes
168
+ are skipped, and elements still open at end of input are closed, so a truncated
169
+ download yields the document parsed so far. CDATA content is kept verbatim and
170
+ not entity-decoded; text outside CDATA is.
127
171
 
128
172
  ## Benchmarks
129
173
 
130
- Six generated shapes, each isolating one cost (per-element, per-child, text,
131
- nesting). Methodology, per-shape tables and caveats live in
132
- [`bench/RESULTS.md`](bench/RESULTS.md); rerun with `npm run bench:data && npm run bench`.
133
-
134
- Realistic Overpass-style document, 6.0 MiB, Apple M4 Pro / Node 26:
174
+ Speed on six generated shapes (Overpass-style documents, children-heavy, text,
175
+ nesting), against `txml`, `fast-xml-parser`, `sax` and `htmlparser2`:
135
176
 
136
177
  | workload | xml-event-parser | txml | fast-xml-parser | sax | htmlparser2 |
137
178
  | --- | --- | --- | --- | --- | --- |
138
179
  | tree (parse + walk) | **26.0 ms, 230 MB/s** | 50.6 ms, 118 MB/s | 167.8 ms, 36 MB/s | — | — |
139
- | tree, retained heap | **30.0 MiB** | 42.5 MiB | 35.2 MiB | — | — |
140
180
  | element callbacks | **27.8 ms, 215 MB/s** | — | — | 100.0 ms, 60 MB/s | 44.9 ms, 133 MB/s |
141
181
  | streamed, 64 KiB chunks | **27.3 ms, 219 MB/s** | 77.4 ms, 77 MB/s | — | 103.3 ms, 58 MB/s | 50.8 ms, 118 MB/s |
142
182
 
143
- Notes on reading the table:
144
-
145
- - The callback and streamed rows for `xml-event-parser` use `retain: 'none'`, so
146
- they do not build a document either; the tree row is the only one that holds
147
- memory. `txml`'s streaming API is a Node stream (`transformStream`), measured
148
- separately and asynchronously.
149
- - Text-heavy documents with many entities and CDATA used to be the weak shape;
150
- replacing the regex decoder with a manual scan took it from 325 to 442 MB/s,
151
- ahead of `txml` (382 MB/s).
152
-
153
- ## Status
154
-
155
- Extracted from [osm2geojson-lite](https://github.com/tibetty/osm2geojson-lite),
156
- where this parser served until 2.2.0 and was then replaced by a purpose-built
157
- OSM scanner. The code and its original unit tests are unchanged apart from the
158
- public naming (`children`/`tagName`/`text` instead of `$innerNodes`/`$tag`/
159
- `$innerText`) and the removal of the `new Function`-based conditioned-path
160
- events.
161
-
162
- Not published yet. The release workflow publishes only when the repository
163
- variable `NPM_TRUSTED_PUBLISHER` is `true`; before that, a release is published
164
- locally because npm cannot configure a trusted publisher for a package that does
165
- not exist yet.
166
-
167
- ## Development
168
-
169
- ```sh
170
- npm ci
171
- npm test # unit tests
172
- npm run typecheck
173
- npm run build # dist/index.mjs, dist/index.cjs, dist/index.d.ts
174
- npm run bench:data # generate ~27 MiB benchmark corpus (gitignored)
175
- npm run bench # compare against txml / sax / htmlparser2 / fast-xml-parser
176
- ```
177
-
178
- ## Releasing
179
-
180
- `release.yml` publishes with npm trusted publishing (OIDC) and is gated on the
181
- repository variable `NPM_TRUSTED_PUBLISHER`, which stays unset until a trusted
182
- publisher exists.
183
-
184
- One-time bootstrap, because npm cannot attach a trusted publisher to a package
185
- that does not exist yet:
186
-
187
- 1. Publish the first version locally:
188
-
189
- ```sh
190
- npm login
191
- npm publish --access public
192
- ```
193
-
194
- 2. npmjs.com -> Packages -> `xml-event-parser` -> Settings -> **Trusted
195
- publishing** -> GitHub Actions:
196
-
197
- | field | value |
198
- | --- | --- |
199
- | Organization or user | `tibetty` |
200
- | Repository | `xml-event-parser` |
201
- | Workflow filename | `release.yml` (filename only, case-sensitive) |
202
- | Environment name | leave empty |
203
- | Allowed actions | tick **npm publish** |
204
-
205
- Configurations created after September 2026 default to `npm stage publish`
206
- only, so `npm publish` has to be ticked explicitly. The first publish through
207
- that workflow must complete within **two days**, or the configuration expires.
208
-
209
- 3. GitHub -> Settings -> Secrets and variables -> Actions -> Variables: add
210
- `NPM_TRUSTED_PUBLISHER` = `true`.
183
+ Full tables, methodology and caveats: [`bench/RESULTS.md`](bench/RESULTS.md).
211
184
 
212
- 4. Optional hardening, once a workflow publish has succeeded: npmjs.com ->
213
- package Settings -> Publishing access -> *Require two-factor authentication
214
- and disallow tokens*.
185
+ ## Contributing
215
186
 
216
- After that, changing `version` in `package.json` on `main` publishes through
217
- `release.yml`, tags `v<version>` and creates the GitHub release. npm generates
218
- provenance automatically only for a public repository; from a private one the
219
- package is published without an attestation.
187
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development commands, tests,
188
+ benchmarks and the release process.
220
189
 
221
190
  ## License
222
191
 
223
- MIT. Includes work contributed to `osm2geojson-lite` by Harel Mazor.
192
+ MIT. Includes work contributed to
193
+ [osm2geojson-lite](https://github.com/tibetty/osm2geojson-lite) by Harel Mazor.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function t(t){let n=t.indexOf(`&`);if(n<0)return t;let r=t.slice(0,n);for(;n<t.length;){let i=t.indexOf(`;`,n+1);if(i<0){r+=t.slice(n);break}let a=e(t.slice(n+1,i));if(r+=a===null?t.slice(n,i+1):a,n=t.indexOf(`&`,i+1),n<0){r+=t.slice(i+1);break}r+=t.slice(i+1,n)}return r}function n(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:t(e);let n=``,r=0;for(;r<e.length;){let i=e.indexOf(`<![CDATA[`,r);if(i<0){n+=t(e.slice(r));break}i>r&&(n+=t(e.slice(r,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){n+=e.slice(i+9);break}n+=e.slice(i+9,a),r=a+3}return n}function r(e){return e===32||e===9||e===10||e===13}var i=class{constructor(e){this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t){if(this.retain===`none`)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let n=t.children;n?n.push(e):t.children=[e]}consume(e){let t=this.buffer,r=t.length,i=this.progressive,a=i&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<r;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=r))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=r;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=n(t.slice(l.textStart,c))),i&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let n=t.indexOf(`>`,o);if(n<0){if(!e)break;o=r;break}o=n+1,s=-1;continue}if(l===33){let n;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(n=t.indexOf(`-->`,o+3),n=n<0?-1:n+3):t.startsWith(`![CDATA[`,o)?(n=t.indexOf(`]]>`,o+9),n=n<0?-1:n+3):(n=t.indexOf(`>`,o),n=n<0?-1:n+1),n<0){if(!e)break;o=r;break}o=n,s=-1;continue}let u=this.parseOpenTag(t,o,r,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(i){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h={node:d,fullTag:m,textStart:o};this.stack.push(h),this.top=h}this.cursor=e?r:s>=0?s:o,e||this.compact()}parseOpenTag(e,n,i,a){let o=n;for(;o<i;){let t=e.charCodeAt(o);if(r(t)||t===47||t===62)break;o++}if(o>=i&&!a)return null;let s=e.slice(n,o),c={tagName:s},l=!1;for(;o<i;){let n=e.charCodeAt(o);for(;o<i&&r(n);)n=e.charCodeAt(++o);if(o>=i)break;if(n===62)return o++,{node:c,tag:s,selfClose:l,end:o};if(n===47){l=!0,o++;continue}let u=o;for(;o<i&&(n=e.charCodeAt(o),!(n===61||r(n)||n===62||n===47));)o++;if(o>=i&&!a)return null;let d=e.slice(u,o);for(;o<i&&r(e.charCodeAt(o));)o++;if(o>=i&&!a)return null;if(e.charCodeAt(o)===61){for(o++;o<i&&r(e.charCodeAt(o));)o++;if(o>=i&&!a)return null;let n=e.charCodeAt(o);if(n===34||n===39){o++;let r=e.indexOf(n===34?`"`:`'`,o);if(r<0){if(!a)return null;r=i}let s=e.slice(o,r);c[d]=s.indexOf(`&`)<0?s:t(s),o=r+1}}else d&&(c[d]=``)}return a?{node:c,tag:s,selfClose:l,end:o}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let r=this.stack.pop(),i=r.node;!r.hasChild&&t>r.textStart&&(i.text=n(e.slice(r.textStart,t)),t=r.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(i,o)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};exports.XmlParser=i,exports.decodeEntities=t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function t(t){let n=t.indexOf(`&`);if(n<0)return t;let r=t.slice(0,n);for(;n<t.length;){let i=t.indexOf(`;`,n+1);if(i<0){r+=t.slice(n);break}let a=e(t.slice(n+1,i));if(r+=a===null?t.slice(n,i+1):a,n=t.indexOf(`&`,i+1),n<0){r+=t.slice(i+1);break}r+=t.slice(i+1,n)}return r}function n(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:t(e);let n=``,r=0;for(;r<e.length;){let i=e.indexOf(`<![CDATA[`,r);if(i<0){n+=t(e.slice(r));break}i>r&&(n+=t(e.slice(r,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){n+=e.slice(i+9);break}n+=e.slice(i+9,a),r=a+3}return n}function r(e){let t=0,n=e.length;return e.charCodeAt(0)===60&&(t=e.charCodeAt(1)===47?2:1),e.charCodeAt(n-1)===62&&n--,e.slice(t,n)}function i(e){return e===32||e===9||e===10||e===13}var a=class{constructor(e){if(this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.keepPaths=/* @__PURE__ */ new Set,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain),e.keep))for(let t of e.keep)this.keepPaths.add(r(t))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t,n){if(this.retain===`none`&&!n)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let r=t.children;r?r.push(e):t.children=[e]}consume(e){let t=this.buffer,r=t.length,i=this.progressive,a=i&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<r;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=r))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=r;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=n(t.slice(l.textStart,c))),i&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f,d?d.keep===!0:!1),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let n=t.indexOf(`>`,o);if(n<0){if(!e)break;o=r;break}o=n+1,s=-1;continue}if(l===33){let n;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(n=t.indexOf(`-->`,o+3),n=n<0?-1:n+3):t.startsWith(`![CDATA[`,o)?(n=t.indexOf(`]]>`,o+9),n=n<0?-1:n+3):(n=t.indexOf(`>`,o),n=n<0?-1:n+1),n<0){if(!e)break;o=r;break}o=n,s=-1;continue}let u=this.parseOpenTag(t,o,r,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(i){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p,this.top?this.top.keep===!0:!1),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h=this.top!==void 0&&this.top.keep===!0||this.keepPaths.has(m),g={node:d,fullTag:m,textStart:o,keep:h};this.stack.push(g),this.top=g}this.cursor=e?r:s>=0?s:o,e||this.compact()}parseOpenTag(e,n,r,a){let o=n;for(;o<r;){let t=e.charCodeAt(o);if(i(t)||t===47||t===62)break;o++}if(o>=r&&!a)return null;let s=e.slice(n,o),c={tagName:s},l=!1;for(;o<r;){let n=e.charCodeAt(o);for(;o<r&&i(n);)n=e.charCodeAt(++o);if(o>=r)break;if(n===62)return o++,{node:c,tag:s,selfClose:l,end:o};if(n===47){l=!0,o++;continue}let u=o;for(;o<r&&(n=e.charCodeAt(o),!(n===61||i(n)||n===62||n===47));)o++;if(o>=r&&!a)return null;let d=e.slice(u,o);for(;o<r&&i(e.charCodeAt(o));)o++;if(o>=r&&!a)return null;if(e.charCodeAt(o)===61){for(o++;o<r&&i(e.charCodeAt(o));)o++;if(o>=r&&!a)return null;let n=e.charCodeAt(o);if(n===34||n===39){o++;let i=e.indexOf(n===34?`"`:`'`,o);if(i<0){if(!a)return null;i=r}let s=e.slice(o,i);c[d]=s.indexOf(`&`)<0?s:t(s),o=i+1}}else d&&(c[d]=``)}return a?{node:c,tag:s,selfClose:l,end:o}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let r=this.stack.pop(),i=r.node;!r.hasChild&&t>r.textStart&&(i.text=n(e.slice(r.textStart,t)),t=r.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(i,o,a?a.keep===!0:!1)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};exports.XmlParser=a,exports.decodeEntities=t;
package/dist/index.d.ts CHANGED
@@ -38,11 +38,21 @@ interface XmlParserOptions {
38
38
  * nobody listens for is parsed and discarded.
39
39
  */
40
40
  retain?: Retain;
41
+ /**
42
+ * Under `retain: 'none'`, elements whose dotted path is listed here keep
43
+ * their subtree (children and text), so a streamed element arrives
44
+ * complete instead of as a bare tag. Paths use the event form without
45
+ * brackets: `'testsuites.testsuite.testcase'`.
46
+ *
47
+ * Memory stays bounded by the largest matched subtree, not the document.
48
+ */
49
+ keep?: string[];
41
50
  }
42
51
  export declare class XmlParser {
43
52
  private queryParent;
44
53
  private progressive;
45
54
  private retain;
55
+ private keepPaths;
46
56
  private parentMap;
47
57
  private evtListeners;
48
58
  /**
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- function e(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function t(t){let n=t.indexOf(`&`);if(n<0)return t;let r=t.slice(0,n);for(;n<t.length;){let i=t.indexOf(`;`,n+1);if(i<0){r+=t.slice(n);break}let a=e(t.slice(n+1,i));if(r+=a===null?t.slice(n,i+1):a,n=t.indexOf(`&`,i+1),n<0){r+=t.slice(i+1);break}r+=t.slice(i+1,n)}return r}function n(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:t(e);let n=``,r=0;for(;r<e.length;){let i=e.indexOf(`<![CDATA[`,r);if(i<0){n+=t(e.slice(r));break}i>r&&(n+=t(e.slice(r,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){n+=e.slice(i+9);break}n+=e.slice(i+9,a),r=a+3}return n}function r(e){return e===32||e===9||e===10||e===13}var i=class{constructor(e){this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t){if(this.retain===`none`)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let n=t.children;n?n.push(e):t.children=[e]}consume(e){let t=this.buffer,r=t.length,i=this.progressive,a=i&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<r;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=r))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=r;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=n(t.slice(l.textStart,c))),i&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let n=t.indexOf(`>`,o);if(n<0){if(!e)break;o=r;break}o=n+1,s=-1;continue}if(l===33){let n;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(n=t.indexOf(`-->`,o+3),n=n<0?-1:n+3):t.startsWith(`![CDATA[`,o)?(n=t.indexOf(`]]>`,o+9),n=n<0?-1:n+3):(n=t.indexOf(`>`,o),n=n<0?-1:n+1),n<0){if(!e)break;o=r;break}o=n,s=-1;continue}let u=this.parseOpenTag(t,o,r,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(i){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h={node:d,fullTag:m,textStart:o};this.stack.push(h),this.top=h}this.cursor=e?r:s>=0?s:o,e||this.compact()}parseOpenTag(e,n,i,a){let o=n;for(;o<i;){let t=e.charCodeAt(o);if(r(t)||t===47||t===62)break;o++}if(o>=i&&!a)return null;let s=e.slice(n,o),c={tagName:s},l=!1;for(;o<i;){let n=e.charCodeAt(o);for(;o<i&&r(n);)n=e.charCodeAt(++o);if(o>=i)break;if(n===62)return o++,{node:c,tag:s,selfClose:l,end:o};if(n===47){l=!0,o++;continue}let u=o;for(;o<i&&(n=e.charCodeAt(o),!(n===61||r(n)||n===62||n===47));)o++;if(o>=i&&!a)return null;let d=e.slice(u,o);for(;o<i&&r(e.charCodeAt(o));)o++;if(o>=i&&!a)return null;if(e.charCodeAt(o)===61){for(o++;o<i&&r(e.charCodeAt(o));)o++;if(o>=i&&!a)return null;let n=e.charCodeAt(o);if(n===34||n===39){o++;let r=e.indexOf(n===34?`"`:`'`,o);if(r<0){if(!a)return null;r=i}let s=e.slice(o,r);c[d]=s.indexOf(`&`)<0?s:t(s),o=r+1}}else d&&(c[d]=``)}return a?{node:c,tag:s,selfClose:l,end:o}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let r=this.stack.pop(),i=r.node;!r.hasChild&&t>r.textStart&&(i.text=n(e.slice(r.textStart,t)),t=r.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(i,o)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};export{i as XmlParser,t as decodeEntities};
1
+ function e(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function t(t){let n=t.indexOf(`&`);if(n<0)return t;let r=t.slice(0,n);for(;n<t.length;){let i=t.indexOf(`;`,n+1);if(i<0){r+=t.slice(n);break}let a=e(t.slice(n+1,i));if(r+=a===null?t.slice(n,i+1):a,n=t.indexOf(`&`,i+1),n<0){r+=t.slice(i+1);break}r+=t.slice(i+1,n)}return r}function n(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:t(e);let n=``,r=0;for(;r<e.length;){let i=e.indexOf(`<![CDATA[`,r);if(i<0){n+=t(e.slice(r));break}i>r&&(n+=t(e.slice(r,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){n+=e.slice(i+9);break}n+=e.slice(i+9,a),r=a+3}return n}function r(e){let t=0,n=e.length;return e.charCodeAt(0)===60&&(t=e.charCodeAt(1)===47?2:1),e.charCodeAt(n-1)===62&&n--,e.slice(t,n)}function i(e){return e===32||e===9||e===10||e===13}var a=class{constructor(e){if(this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.keepPaths=/* @__PURE__ */ new Set,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain),e.keep))for(let t of e.keep)this.keepPaths.add(r(t))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t,n){if(this.retain===`none`&&!n)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let r=t.children;r?r.push(e):t.children=[e]}consume(e){let t=this.buffer,r=t.length,i=this.progressive,a=i&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<r;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=r))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=r;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=n(t.slice(l.textStart,c))),i&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f,d?d.keep===!0:!1),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let n=t.indexOf(`>`,o);if(n<0){if(!e)break;o=r;break}o=n+1,s=-1;continue}if(l===33){let n;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(n=t.indexOf(`-->`,o+3),n=n<0?-1:n+3):t.startsWith(`![CDATA[`,o)?(n=t.indexOf(`]]>`,o+9),n=n<0?-1:n+3):(n=t.indexOf(`>`,o),n=n<0?-1:n+1),n<0){if(!e)break;o=r;break}o=n,s=-1;continue}let u=this.parseOpenTag(t,o,r,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(i){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p,this.top?this.top.keep===!0:!1),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h=this.top!==void 0&&this.top.keep===!0||this.keepPaths.has(m),g={node:d,fullTag:m,textStart:o,keep:h};this.stack.push(g),this.top=g}this.cursor=e?r:s>=0?s:o,e||this.compact()}parseOpenTag(e,n,r,a){let o=n;for(;o<r;){let t=e.charCodeAt(o);if(i(t)||t===47||t===62)break;o++}if(o>=r&&!a)return null;let s=e.slice(n,o),c={tagName:s},l=!1;for(;o<r;){let n=e.charCodeAt(o);for(;o<r&&i(n);)n=e.charCodeAt(++o);if(o>=r)break;if(n===62)return o++,{node:c,tag:s,selfClose:l,end:o};if(n===47){l=!0,o++;continue}let u=o;for(;o<r&&(n=e.charCodeAt(o),!(n===61||i(n)||n===62||n===47));)o++;if(o>=r&&!a)return null;let d=e.slice(u,o);for(;o<r&&i(e.charCodeAt(o));)o++;if(o>=r&&!a)return null;if(e.charCodeAt(o)===61){for(o++;o<r&&i(e.charCodeAt(o));)o++;if(o>=r&&!a)return null;let n=e.charCodeAt(o);if(n===34||n===39){o++;let i=e.indexOf(n===34?`"`:`'`,o);if(i<0){if(!a)return null;i=r}let s=e.slice(o,i);c[d]=s.indexOf(`&`)<0?s:t(s),o=i+1}}else d&&(c[d]=``)}return a?{node:c,tag:s,selfClose:l,end:o}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let r=this.stack.pop(),i=r.node;!r.hasChild&&t>r.textStart&&(i.text=n(e.slice(r.textStart,t)),t=r.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(i,o,a?a.keep===!0:!1)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};export{a as XmlParser,t as decodeEntities};
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:stream"),t=require("node:string_decoder");function n(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function r(e){let t=e.indexOf(`&`);if(t<0)return e;let r=e.slice(0,t);for(;t<e.length;){let i=e.indexOf(`;`,t+1);if(i<0){r+=e.slice(t);break}let a=n(e.slice(t+1,i));if(r+=a===null?e.slice(t,i+1):a,t=e.indexOf(`&`,i+1),t<0){r+=e.slice(i+1);break}r+=e.slice(i+1,t)}return r}function i(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:r(e);let t=``,n=0;for(;n<e.length;){let i=e.indexOf(`<![CDATA[`,n);if(i<0){t+=r(e.slice(n));break}i>n&&(t+=r(e.slice(n,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){t+=e.slice(i+9);break}t+=e.slice(i+9,a),n=a+3}return t}function a(e){let t=0,n=e.length;return e.charCodeAt(0)===60&&(t=e.charCodeAt(1)===47?2:1),e.charCodeAt(n-1)===62&&n--,e.slice(t,n)}function o(e){return e===32||e===9||e===10||e===13}var s=class{constructor(e){if(this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.keepPaths=/* @__PURE__ */ new Set,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain),e.keep))for(let t of e.keep)this.keepPaths.add(a(t))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t,n){if(this.retain===`none`&&!n)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let r=t.children;r?r.push(e):t.children=[e]}consume(e){let t=this.buffer,n=t.length,r=this.progressive,a=r&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<n;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=n))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=n;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=i(t.slice(l.textStart,c))),r&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f,d?d.keep===!0:!1),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let r=t.indexOf(`>`,o);if(r<0){if(!e)break;o=n;break}o=r+1,s=-1;continue}if(l===33){let r;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(r=t.indexOf(`-->`,o+3),r=r<0?-1:r+3):t.startsWith(`![CDATA[`,o)?(r=t.indexOf(`]]>`,o+9),r=r<0?-1:r+3):(r=t.indexOf(`>`,o),r=r<0?-1:r+1),r<0){if(!e)break;o=n;break}o=r,s=-1;continue}let u=this.parseOpenTag(t,o,n,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(r){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p,this.top?this.top.keep===!0:!1),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h=this.top!==void 0&&this.top.keep===!0||this.keepPaths.has(m),g={node:d,fullTag:m,textStart:o,keep:h};this.stack.push(g),this.top=g}this.cursor=e?n:s>=0?s:o,e||this.compact()}parseOpenTag(e,t,n,i){let a=t;for(;a<n;){let t=e.charCodeAt(a);if(o(t)||t===47||t===62)break;a++}if(a>=n&&!i)return null;let s=e.slice(t,a),c={tagName:s},l=!1;for(;a<n;){let t=e.charCodeAt(a);for(;a<n&&o(t);)t=e.charCodeAt(++a);if(a>=n)break;if(t===62)return a++,{node:c,tag:s,selfClose:l,end:a};if(t===47){l=!0,a++;continue}let u=a;for(;a<n&&(t=e.charCodeAt(a),!(t===61||o(t)||t===62||t===47));)a++;if(a>=n&&!i)return null;let d=e.slice(u,a);for(;a<n&&o(e.charCodeAt(a));)a++;if(a>=n&&!i)return null;if(e.charCodeAt(a)===61){for(a++;a<n&&o(e.charCodeAt(a));)a++;if(a>=n&&!i)return null;let t=e.charCodeAt(a);if(t===34||t===39){a++;let o=e.indexOf(t===34?`"`:`'`,a);if(o<0){if(!i)return null;o=n}let s=e.slice(a,o);c[d]=s.indexOf(`&`)<0?s:r(s),a=o+1}}else d&&(c[d]=``)}return i?{node:c,tag:s,selfClose:l,end:a}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let n=this.stack.pop(),r=n.node;!n.hasChild&&t>n.textStart&&(r.text=i(e.slice(n.textStart,t)),t=n.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(r,o,a?a.keep===!0:!1)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};function c(e){if(typeof e!=`string`||e.length===0)throw TypeError(`createStream() requires { event }, e.g. { event: "</root.item>" }`);return e}function l(n){let r=c(n&&n.event),i=new s({...n,progressive:!0,retain:`none`,keep:[r]}),a=new t.StringDecoder(`utf8`),o=new e.Transform({readableObjectMode:!0,transform(e,t,n){try{i.write(typeof e==`string`?e:a.write(e)),n()}catch(e){n(e)}},flush(e){try{let t=a.end();t.length>0&&i.write(t),i.end(),e()}catch(t){e(t)}}});return i.on(r,e=>{o.push(e)}),o}exports.createStream=l;
@@ -0,0 +1,61 @@
1
+ import { Transform } from "node:stream";
2
+ type Retain = 'all' | 'root' | 'none';
3
+ interface XmlParserOptions {
4
+ /** Emit `<path>` / `</path>` events as elements open and close. Default `false`. */
5
+ progressive?: boolean;
6
+ /** Track parents so `getParent(node)` works. Default `false`. */
7
+ queryParent?: boolean;
8
+ /**
9
+ * What the parser keeps after an element closes:
10
+ * - `'all'` (default) the whole document tree, returned by `end()`
11
+ * - `'root'` the root being built; a finished top-level element is emitted
12
+ * and dropped, so memory is bounded by the largest root
13
+ * - `'none'` nothing; every element is emitted and dropped, so memory is
14
+ * bounded by the nesting depth. Combine with listeners: an element
15
+ * nobody listens for is parsed and discarded.
16
+ */
17
+ retain?: Retain;
18
+ /**
19
+ * Under `retain: 'none'`, elements whose dotted path is listed here keep
20
+ * their subtree (children and text), so a streamed element arrives
21
+ * complete instead of as a bare tag. Paths use the event form without
22
+ * brackets: `'testsuites.testsuite.testcase'`.
23
+ *
24
+ * Memory stays bounded by the largest matched subtree, not the document.
25
+ */
26
+ keep?: string[];
27
+ }
28
+ export interface XmlStreamOptions extends XmlParserOptions {
29
+ /**
30
+ * Close-event path to emit, in event form: `'</testsuites.testsuite.testcase>'`.
31
+ *
32
+ * Required, and it is what keeps memory bounded: elements matching the path
33
+ * are handed out whole (with their children) and everything else is
34
+ * dropped as it closes. Memory is the largest matched element, not the file.
35
+ */
36
+ event: string;
37
+ }
38
+ /**
39
+ * Parse XML from a readable stream, emitting one object per matching element.
40
+ *
41
+ * ```js
42
+ * import { createStream } from 'xml-event-parser/stream';
43
+ *
44
+ * pipeline(
45
+ * createReadStream('junit.xml'),
46
+ * createStream({ event: '</testsuites.testsuite.testcase>' }),
47
+ * new Writable({ objectMode: true, write(testcase, _enc, done) { ...; done(); } }),
48
+ * );
49
+ * ```
50
+ *
51
+ * Chunks may split anywhere; multibyte characters split across chunk boundaries
52
+ * are reassembled with `StringDecoder`. Backpressure is applied per chunk: the
53
+ * stream stops pulling input while the readable side is full, so a slow consumer
54
+ * does not accumulate the document.
55
+ *
56
+ * This module imports `node:stream`, which is why it is a separate entry point
57
+ * (`xml-event-parser/stream`): the core parser stays usable in browsers and edge
58
+ * runtimes that expose no Node built-ins.
59
+ */
60
+ export declare function createStream(options: XmlStreamOptions): Transform;
61
+ export {};
@@ -0,0 +1 @@
1
+ import{Transform as e}from"node:stream";import{StringDecoder as t}from"node:string_decoder";function n(e){switch(e){case`amp`:return`&`;case`lt`:return`<`;case`gt`:return`>`;case`quot`:return`"`;case`apos`:return`'`}if(e.charCodeAt(0)===35){let t=e.charCodeAt(1)===120||e.charCodeAt(1)===88?parseInt(e.slice(2),16):parseInt(e.slice(1),10);if(Number.isFinite(t)&&t>=0&&t<=1114111)return String.fromCodePoint(t)}return null}function r(e){let t=e.indexOf(`&`);if(t<0)return e;let r=e.slice(0,t);for(;t<e.length;){let i=e.indexOf(`;`,t+1);if(i<0){r+=e.slice(t);break}let a=n(e.slice(t+1,i));if(r+=a===null?e.slice(t,i+1):a,t=e.indexOf(`&`,i+1),t<0){r+=e.slice(i+1);break}r+=e.slice(i+1,t)}return r}function i(e){if(e.indexOf(`<![CDATA[`)<0)return e.indexOf(`&`)<0?e:r(e);let t=``,n=0;for(;n<e.length;){let i=e.indexOf(`<![CDATA[`,n);if(i<0){t+=r(e.slice(n));break}i>n&&(t+=r(e.slice(n,i)));let a=e.indexOf(`]]>`,i+9);if(a<0){t+=e.slice(i+9);break}t+=e.slice(i+9,a),n=a+3}return t}function a(e){let t=0,n=e.length;return e.charCodeAt(0)===60&&(t=e.charCodeAt(1)===47?2:1),e.charCodeAt(n-1)===62&&n--,e.slice(t,n)}function o(e){return e===32||e===9||e===10||e===13}var s=class{constructor(e){if(this.queryParent=!1,this.progressive=!1,this.retain=`all`,this.keepPaths=/* @__PURE__ */ new Set,this.parentMap=/* @__PURE__ */ new WeakMap,this.evtListeners={},this.openListenerCount=0,this.watchedPaths=/* @__PURE__ */ new Set,this.watchedChildren=/* @__PURE__ */ new Map,this.watchedTopLevel=/* @__PURE__ */ new Set,this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=``,this.active=!1,e&&(this.queryParent=e.queryParent===!0,this.progressive=e.progressive===!0,e.retain&&(this.retain=e.retain),e.keep))for(let t of e.keep)this.keepPaths.add(a(t))}write(e){return this.active||this.begin(void 0,void 0),e.length===0||(this.buffer.length===0?this.buffer=e:this.buffer+=e,this.consume(!1)),this}end(){if(!this.active)return[];this.consume(!0),this.finish();let e=this.roots;return this.reset(),e}parse(e,t,n){return this.begin(t,n),this.write(e),this.end()}begin(e,t){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.basePath=t||``,this.top=e?{node:e,fullTag:this.basePath,textStart:0}:void 0,this.rootOwner=this.top,this.parentMap=/* @__PURE__ */ new WeakMap,this.active=!0}reset(){this.buffer=``,this.cursor=0,this.stack=[],this.roots=[],this.top=void 0,this.rootOwner=void 0,this.basePath=``,this.active=!1}attach(e,t,n){if(this.retain===`none`&&!n)return;if(t===void 0){this.retain===`all`&&this.roots.push(e);return}let r=t.children;r?r.push(e):t.children=[e]}consume(e){let t=this.buffer,n=t.length,r=this.progressive,a=r&&this.openListenerCount>0,o=this.cursor,s=-1;for(;o<n;){let c=t.indexOf(`<`,o);if(c<0||(s=c,o=c+1,o>=n))break;let l=t.charCodeAt(o);if(l===47){let a=t.indexOf(`>`,o);if(a<0){if(!e)break;o=n;break}let l=this.stack.pop();if(o=a+1,s=-1,!l)continue;let u=l.node,d=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,f=d?d.node:void 0;!l.hasChild&&c>l.textStart&&(u.text=i(t.slice(l.textStart,c))),r&&this.watchedPaths.has(l.fullTag)&&this.emit(`</`+l.fullTag+`>`,u,f),this.attach(u,f,d?d.keep===!0:!1),d&&(d.hasChild=!0),this.top=d;continue}if(l===63){let r=t.indexOf(`>`,o);if(r<0){if(!e)break;o=n;break}o=r+1,s=-1;continue}if(l===33){let r;if(t.charCodeAt(o+1)===45&&t.charCodeAt(o+2)===45?(r=t.indexOf(`-->`,o+3),r=r<0?-1:r+3):t.startsWith(`![CDATA[`,o)?(r=t.indexOf(`]]>`,o+9),r=r<0?-1:r+3):(r=t.indexOf(`>`,o),r=r<0?-1:r+1),r<0){if(!e)break;o=n;break}o=r,s=-1;continue}let u=this.parseOpenTag(t,o,n,e);if(u===null)break;o=u.end,s=-1;let d=u.node,f=u.tag,p=this.top?this.top.node:void 0;if(this.queryParent&&p&&this.parentMap.set(d,p),u.selfClose){if(r){let e=this.top?this.top.fullTag:this.basePath,t=e?this.watchedChildren.get(e):this.watchedTopLevel;if(t&&t.has(f)){let t=e?e+`.`+f:f;a&&this.emit(`<`+t+`>`,d,p),this.emit(`</`+t+`>`,d,p)}}this.attach(d,p,this.top?this.top.keep===!0:!1),this.top&&(this.top.hasChild=!0);continue}let m=this.top?this.top.fullTag?this.top.fullTag+`.`+f:f:this.basePath?this.basePath+`.`+f:f;a&&this.watchedPaths.has(m)&&this.emit(`<`+m+`>`,d,p);let h=this.top!==void 0&&this.top.keep===!0||this.keepPaths.has(m),g={node:d,fullTag:m,textStart:o,keep:h};this.stack.push(g),this.top=g}this.cursor=e?n:s>=0?s:o,e||this.compact()}parseOpenTag(e,t,n,i){let a=t;for(;a<n;){let t=e.charCodeAt(a);if(o(t)||t===47||t===62)break;a++}if(a>=n&&!i)return null;let s=e.slice(t,a),c={tagName:s},l=!1;for(;a<n;){let t=e.charCodeAt(a);for(;a<n&&o(t);)t=e.charCodeAt(++a);if(a>=n)break;if(t===62)return a++,{node:c,tag:s,selfClose:l,end:a};if(t===47){l=!0,a++;continue}let u=a;for(;a<n&&(t=e.charCodeAt(a),!(t===61||o(t)||t===62||t===47));)a++;if(a>=n&&!i)return null;let d=e.slice(u,a);for(;a<n&&o(e.charCodeAt(a));)a++;if(a>=n&&!i)return null;if(e.charCodeAt(a)===61){for(a++;a<n&&o(e.charCodeAt(a));)a++;if(a>=n&&!i)return null;let t=e.charCodeAt(a);if(t===34||t===39){a++;let o=e.indexOf(t===34?`"`:`'`,a);if(o<0){if(!i)return null;o=n}let s=e.slice(a,o);c[d]=s.indexOf(`&`)<0?s:r(s),a=o+1}}else d&&(c[d]=``)}return i?{node:c,tag:s,selfClose:l,end:a}:null}finish(){let e=this.buffer,t=e.length;for(;this.stack.length>0;){let n=this.stack.pop(),r=n.node;!n.hasChild&&t>n.textStart&&(r.text=i(e.slice(n.textStart,t)),t=n.textStart);let a=this.stack.length?this.stack[this.stack.length-1]:this.rootOwner,o=a?a.node:void 0;this.attach(r,o,a?a.keep===!0:!1)}}compact(){let e=this.cursor;for(let t=this.stack.length-1;t>=0;t--){let n=this.stack[t];if(!n.hasChild){e=n.textStart;break}}if(!(e<=0)){this.buffer=this.buffer.slice(e),this.cursor-=e;for(let t=0;t<this.stack.length;t++){let n=this.stack[t];n.textStart=n.textStart>e?n.textStart-e:0}this.rootOwner&&(this.rootOwner.textStart=0)}}getParent(e){return this.queryParent?this.parentMap.get(e):null}addListener(e,t){this.$addListener(e,t)}removeListener(e,t){this.$removeListener(e,t)}on(e,t){this.addListener(e,t)}off(e,t){this.removeListener(e,t)}$addListener(e,t){let n=this.evtListeners[e];if(n?n.push(t):this.evtListeners[e]=[t],e.charCodeAt(1)!==47&&this.openListenerCount++,e.charCodeAt(0)===60){let t=e.slice(e.charCodeAt(1)===47?2:1,e.length-1);this.watchedPaths.add(t);let n=t.lastIndexOf(`.`);if(n<0)this.watchedTopLevel.add(t);else{let e=t.slice(0,n),r=this.watchedChildren.get(e);r||(r=/* @__PURE__ */ new Set,this.watchedChildren.set(e,r)),r.add(t.slice(n+1))}}}$removeListener(e,t){let n=this.evtListeners[e],r=-1;n&&(r=n.indexOf(t)),r>=0&&(n.splice(r,1),e.charCodeAt(1)!==47&&this.openListenerCount--)}emit(e,t,n){let r=this.evtListeners[e];if(r)for(let e=0;e<r.length;e++)r[e](t,n)}};function c(e){if(typeof e!=`string`||e.length===0)throw TypeError(`createStream() requires { event }, e.g. { event: "</root.item>" }`);return e}function l(n){let r=c(n&&n.event),i=new s({...n,progressive:!0,retain:`none`,keep:[r]}),a=new t(`utf8`),o=new e({readableObjectMode:!0,transform(e,t,n){try{i.write(typeof e==`string`?e:a.write(e)),n()}catch(e){n(e)}},flush(e){try{let t=a.end();t.length>0&&i.write(t),i.end(),e()}catch(t){e(t)}}});return i.on(r,e=>{o.push(e)}),o}export{l as createStream};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-event-parser",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Tiny dependency-free XML parser with a tree and path-based events in one pass",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,9 +13,15 @@
13
13
  "keywords": [
14
14
  "xml",
15
15
  "parser",
16
- "events",
17
- "tree",
18
16
  "streaming",
17
+ "sax",
18
+ "event",
19
+ "junit",
20
+ "test-report",
21
+ "sitemap",
22
+ "gpx",
23
+ "edge",
24
+ "cloudflare-workers",
19
25
  "zero-dependency"
20
26
  ],
21
27
  "main": "dist/index.cjs",
@@ -25,6 +31,7 @@
25
31
  "typecheck": "tsc --noEmit",
26
32
  "bench:data": "node bench/generate.mjs",
27
33
  "bench": "node --expose-gc bench/run.mjs",
34
+ "bench:memory": "npm run build && node bench/memory.mjs",
28
35
  "prepublishOnly": "npm run build && npm test"
29
36
  },
30
37
  "license": "MIT",
@@ -38,6 +45,7 @@
38
45
  "access": "public"
39
46
  },
40
47
  "devDependencies": {
48
+ "@types/node": "^26.6.6",
41
49
  "fast-xml-parser": "^5.12.1",
42
50
  "htmlparser2": "^12.0.0",
43
51
  "rolldown": "^1.3.2",
@@ -54,6 +62,11 @@
54
62
  "types": "./dist/index.d.ts",
55
63
  "import": "./dist/index.mjs",
56
64
  "require": "./dist/index.cjs"
65
+ },
66
+ "./stream": {
67
+ "types": "./dist/stream.d.ts",
68
+ "import": "./dist/stream.mjs",
69
+ "require": "./dist/stream.cjs"
57
70
  }
58
71
  },
59
72
  "sideEffects": false,