xml-event-parser 0.1.1 → 0.2.0
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 +104 -26
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +1 -1
- package/dist/stream.cjs +1 -0
- package/dist/stream.d.ts +61 -0
- package/dist/stream.mjs +1 -0
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -1,36 +1,97 @@
|
|
|
1
1
|
xml-event-parser
|
|
2
2
|
================
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- **
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
4
|
+
**Stream XML without loading it.** A tiny parser that gives you a document tree
|
|
5
|
+
and path-based events in one pass: constant heap on a 240 MB CI report, 2.2 KiB
|
|
6
|
+
gzipped, zero dependencies, and no `node:*` imports in the core entry.
|
|
7
|
+
|
|
8
|
+
- **Bounded memory**: `retain: 'none'` plus a path subscription keeps only the
|
|
9
|
+
element being read. 240 MB in, 81 MiB of RSS.
|
|
10
|
+
- **Fast**: ~3.7x `sax` and ~4x `fast-xml-parser` on the same report, and faster
|
|
11
|
+
than `txml` on every shape we benchmark ([numbers](bench/RESULTS.md)).
|
|
12
|
+
- **Two views, one pass**: a tree node and an `open`/`close` event per element -
|
|
13
|
+
no second traversal, no DOM.
|
|
14
|
+
- **Small and portable**: 6.4 KiB minified / 2.2 KiB gzipped, zero dependencies,
|
|
15
|
+
no `eval`, works in Node, browsers and edge runtimes (the Node adapter is a
|
|
16
|
+
separate `xml-event-parser/stream` entry).
|
|
17
|
+
|
|
18
|
+
## When to use this
|
|
19
|
+
|
|
20
|
+
| your job | use |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| Read a file bigger than memory: CI/test reports (JUnit, SARIF, coverage), sitemaps, GPX/KML tracks, OSM extracts | **this**, `xml-event-parser/stream` |
|
|
23
|
+
| Pick elements while streaming without writing a SAX state machine | **this**, path events |
|
|
24
|
+
| Parse occasionally in a browser or Worker where a 20 KiB parser is too much | **this**, core entry (2.2 KiB) |
|
|
25
|
+
| Turn a small config XML into a plain object | `fast-xml-parser` or `xml2js` (richer object mapping) |
|
|
26
|
+
| DOM semantics: namespaces, XPath, mutation | `@xmldom/xmldom` |
|
|
27
|
+
| Zero allocation, hand-tuned callbacks, no tree at all | `sax` (slightly less memory than ours, ~3.7x slower here) |
|
|
28
|
+
|
|
29
|
+
## 240 MB in one second
|
|
30
|
+
|
|
31
|
+
A generated JUnit report, each parser in its own process capped at 512 MB of
|
|
32
|
+
heap - a typical CI container ([reproduce it](#development)):
|
|
33
|
+
|
|
34
|
+
| parser | 60 MB report | 240 MB report |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| **xml-event-parser** | **0.27 s, 65 MiB** | **1.03 s, 81 MiB** |
|
|
37
|
+
| sax | 0.96 s, 67 MiB | 3.80 s, 79 MiB |
|
|
38
|
+
| fast-xml-parser | 1.52 s, 637 MiB | **out of memory** |
|
|
39
|
+
|
|
40
|
+
The file grows 4x; the heap does not. `fast-xml-parser` builds a tree, so it
|
|
41
|
+
needs roughly 7-11x the file size and dies on the larger report.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm install xml-event-parser
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Tree, when the document fits:
|
|
20
50
|
|
|
21
51
|
```js
|
|
22
52
|
import { XmlParser } from 'xml-event-parser';
|
|
23
53
|
|
|
24
|
-
const
|
|
54
|
+
const roots = new XmlParser().parse(xml);
|
|
55
|
+
// roots[0].tagName, roots[0].children, roots[0].text, attributes are flat
|
|
56
|
+
```
|
|
25
57
|
|
|
26
|
-
|
|
27
|
-
// way.tagName === 'way', way.id, way.version, way.children ...
|
|
28
|
-
console.log(way.id, way.children?.length ?? 0);
|
|
29
|
-
});
|
|
58
|
+
Stream, when it does not:
|
|
30
59
|
|
|
31
|
-
|
|
60
|
+
```js
|
|
61
|
+
import { createReadStream } from 'node:fs';
|
|
62
|
+
import { Writable } from 'node:stream';
|
|
63
|
+
import { pipeline } from 'node:stream/promises';
|
|
64
|
+
import { createStream } from 'xml-event-parser/stream';
|
|
65
|
+
|
|
66
|
+
await pipeline(
|
|
67
|
+
createReadStream('junit.xml'),
|
|
68
|
+
createStream({ event: '</testsuites.testsuite.testcase>' }),
|
|
69
|
+
new Writable({
|
|
70
|
+
objectMode: true,
|
|
71
|
+
write(testcase, _encoding, done) {
|
|
72
|
+
// complete element: attributes + <failure>/<system-out> children
|
|
73
|
+
if (testcase.children?.some((child) => child.tagName === 'failure')) {
|
|
74
|
+
console.log(testcase.name);
|
|
75
|
+
}
|
|
76
|
+
done();
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Without Node streams, `write()`/`end()` work anywhere:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const parser = new XmlParser({ progressive: true, retain: 'none', keep: ['testsuites.testsuite.testcase'] });
|
|
86
|
+
parser.on('</testsuites.testsuite.testcase>', (testcase) => { /* ... */ });
|
|
87
|
+
for await (const chunk of response.body) parser.write(chunk); // no node: imports
|
|
88
|
+
parser.end();
|
|
32
89
|
```
|
|
33
90
|
|
|
91
|
+
Runnable examples: [`examples/junit.mjs`](examples/junit.mjs),
|
|
92
|
+
[`examples/sitemap.mjs`](examples/sitemap.mjs),
|
|
93
|
+
[`examples/gpx.mjs`](examples/gpx.mjs) - `node examples/junit.mjs`.
|
|
94
|
+
|
|
34
95
|
## Node shape
|
|
35
96
|
|
|
36
97
|
```ts
|
|
@@ -87,6 +148,20 @@ Measured on a 60 MiB document (ten concatenated extracts, 327k elements):
|
|
|
87
148
|
327k elements. Without listeners, `retain: 'none'` still parses - it just throws
|
|
88
149
|
the result away.
|
|
89
150
|
|
|
151
|
+
Under `retain: 'none'`, `keep` restores the subtree of the elements you actually
|
|
152
|
+
want, so a streamed element is complete rather than a bare tag:
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
new XmlParser({
|
|
156
|
+
progressive: true,
|
|
157
|
+
retain: 'none',
|
|
158
|
+
keep: ['testsuites.testsuite.testcase'], // 'testsuites.testsuite.testcase' or '</testsuites.testsuite.testcase>'
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Memory is then the largest matched element, not the document. `createStream`
|
|
163
|
+
sets this up for you from its `event` path.
|
|
164
|
+
|
|
90
165
|
## Events
|
|
91
166
|
|
|
92
167
|
| form | fires |
|
|
@@ -111,6 +186,7 @@ parser.on('</osm.way>', (way) => {
|
|
|
111
186
|
| `progressive` | `false` | emit events. Without it, `parse()` only builds the tree |
|
|
112
187
|
| `queryParent` | `false` | track parents so `parser.getParent(node)` works (costs a `WeakMap`) |
|
|
113
188
|
| `retain` | `'all'` | what survives an element: `'all'`, `'root'` or `'none'` (see [Streaming](#streaming)) |
|
|
189
|
+
| `keep` | `[]` | under `retain: 'none'`, dotted paths whose elements keep their subtree |
|
|
114
190
|
|
|
115
191
|
`parse(xml, parent?, dir?)` also accepts an existing node to attach the parsed
|
|
116
192
|
roots to, and a dotted path prefix used when building event names.
|
|
@@ -159,10 +235,11 @@ public naming (`children`/`tagName`/`text` instead of `$innerNodes`/`$tag`/
|
|
|
159
235
|
`$innerText`) and the removal of the `new Function`-based conditioned-path
|
|
160
236
|
events.
|
|
161
237
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
238
|
+
Published as `xml-event-parser` (current version on npm). Releases are cut by
|
|
239
|
+
changing `version` on `main`: `release.yml` publishes through npm trusted
|
|
240
|
+
publishing (OIDC, with provenance), tags `v<version>` and creates the GitHub
|
|
241
|
+
release. The job runs only when the repository variable
|
|
242
|
+
`NPM_TRUSTED_PUBLISHER` is `true`.
|
|
166
243
|
|
|
167
244
|
## Development
|
|
168
245
|
|
|
@@ -172,6 +249,7 @@ npm test # unit tests
|
|
|
172
249
|
npm run typecheck
|
|
173
250
|
npm run build # dist/index.mjs, dist/index.cjs, dist/index.d.ts
|
|
174
251
|
npm run bench:data # generate ~27 MiB benchmark corpus (gitignored)
|
|
252
|
+
npm run bench:memory # JUnit memory/time table under a 512 MB heap cap
|
|
175
253
|
npm run bench # compare against txml / sax / htmlparser2 / fast-xml-parser
|
|
176
254
|
```
|
|
177
255
|
|
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
|
|
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
|
|
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};
|
package/dist/stream.cjs
ADDED
|
@@ -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;
|
package/dist/stream.d.ts
ADDED
|
@@ -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 {};
|
package/dist/stream.mjs
ADDED
|
@@ -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.
|
|
3
|
+
"version": "0.2.0",
|
|
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,
|