xml-event-parser 0.2.0 → 0.2.2

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
@@ -2,18 +2,14 @@ xml-event-parser
2
2
  ================
3
3
 
4
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).
5
+ and path-based events in one pass.
6
+
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.
17
13
 
18
14
  ## When to use this
19
15
 
@@ -21,15 +17,14 @@ gzipped, zero dependencies, and no `node:*` imports in the core entry.
21
17
  | --- | --- |
22
18
  | Read a file bigger than memory: CI/test reports (JUnit, SARIF, coverage), sitemaps, GPX/KML tracks, OSM extracts | **this**, `xml-event-parser/stream` |
23
19
  | 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) |
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` |
26
22
  | 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) |
23
+ | Hand-tuned callbacks with no tree at all | `sax` |
28
24
 
29
25
  ## 240 MB in one second
30
26
 
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)):
27
+ Generated JUnit reports, each parser in its own process capped at 512 MB of heap:
33
28
 
34
29
  | parser | 60 MB report | 240 MB report |
35
30
  | --- | --- | --- |
@@ -37,25 +32,18 @@ heap - a typical CI container ([reproduce it](#development)):
37
32
  | sax | 0.96 s, 67 MiB | 3.80 s, 79 MiB |
38
33
  | fast-xml-parser | 1.52 s, 637 MiB | **out of memory** |
39
34
 
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.
35
+ The file grows 4x; the heap does not.
42
36
 
43
- ## Quick start
37
+ ## Install
44
38
 
45
39
  ```sh
46
40
  npm install xml-event-parser
47
41
  ```
48
42
 
49
- Tree, when the document fits:
50
-
51
- ```js
52
- import { XmlParser } from 'xml-event-parser';
53
-
54
- const roots = new XmlParser().parse(xml);
55
- // roots[0].tagName, roots[0].children, roots[0].text, attributes are flat
56
- ```
43
+ ## Typical usage
57
44
 
58
- Stream, when it does not:
45
+ Stream a report and act on each element. This is the case the library is built
46
+ for: the file can be any size, memory stays flat.
59
47
 
60
48
  ```js
61
49
  import { createReadStream } from 'node:fs';
@@ -69,9 +57,9 @@ await pipeline(
69
57
  new Writable({
70
58
  objectMode: true,
71
59
  write(testcase, _encoding, done) {
72
- // complete element: attributes + <failure>/<system-out> children
60
+ // testcase is complete: attributes plus <failure>/<system-out> children
73
61
  if (testcase.children?.some((child) => child.tagName === 'failure')) {
74
- console.log(testcase.name);
62
+ console.log(`${testcase.classname} › ${testcase.name}`);
75
63
  }
76
64
  done();
77
65
  },
@@ -79,63 +67,62 @@ await pipeline(
79
67
  );
80
68
  ```
81
69
 
82
- Without Node streams, `write()`/`end()` work anywhere:
70
+ > **The path is the full chain from the document root.** For
71
+ > `<testsuites><testsuite><testcase>` the event is
72
+ > `'</testsuites.testsuite.testcase>'`; `'</testcase>'` matches nothing.
73
+ > `event` also accepts the bare path (`'testsuites.testsuite.testcase'`).
74
+
75
+ Without Node streams - a Worker, Deno, Bun or a plain callback loop:
83
76
 
84
77
  ```js
85
- const parser = new XmlParser({ progressive: true, retain: 'none', keep: ['testsuites.testsuite.testcase'] });
78
+ const parser = new XmlParser({
79
+ progressive: true,
80
+ retain: 'none',
81
+ keep: ['testsuites.testsuite.testcase'],
82
+ });
83
+
86
84
  parser.on('</testsuites.testsuite.testcase>', (testcase) => { /* ... */ });
87
- for await (const chunk of response.body) parser.write(chunk); // no node: imports
85
+
86
+ for await (const chunk of stream) {
87
+ parser.write(chunk);
88
+ }
88
89
  parser.end();
89
90
  ```
90
91
 
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`.
92
+ When the document fits in memory, skip streaming:
94
93
 
95
- ## Node shape
94
+ ```js
95
+ import { XmlParser } from 'xml-event-parser';
96
96
 
97
- ```ts
98
- interface XmlNode {
99
- tagName: string; // element name, e.g. 'way'
100
- children?: XmlNode[]; // present only when the element has child elements
101
- text?: string; // present only for text-only elements
102
- [attribute: string]: any; // attributes, copied flat onto the node
103
- }
97
+ const roots = new XmlParser().parse(xml);
98
+ console.log(roots[0].tagName, roots[0].children);
104
99
  ```
105
100
 
106
- Attributes are flat rather than nested in an `attributes` object on purpose: one
107
- object per element keeps a parsed document small (see the heap column in
108
- [`bench/RESULTS.md`](bench/RESULTS.md)). Names that collide with `tagName`,
109
- `children` or `text` are shadowed by the element's own values; if that matters
110
- for your vocabulary, read attributes off the source XML instead.
101
+ Runnable examples, one per common job: [`examples/junit.mjs`](examples/junit.mjs)
102
+ (test report), [`examples/sitemap.mjs`](examples/sitemap.mjs),
103
+ [`examples/gpx.mjs`](examples/gpx.mjs) (track length).
111
104
 
112
- An element with nested tags is emitted on close **after** its children are
113
- attached, so `node.children` is complete inside a close listener.
105
+ ## API
114
106
 
115
- ## Streaming
107
+ ### Node shape
116
108
 
117
- ```js
118
- import { createReadStream } from 'node:fs';
119
- import { XmlParser } from 'xml-event-parser';
120
-
121
- const parser = new XmlParser({ progressive: true, retain: 'none' });
122
- parser.on('</osm.way>', (way) => {
123
- // one complete element; `way` is released after this returns
124
- });
125
-
126
- for await (const chunk of createReadStream('planet.osm.xml', 'utf8')) {
127
- parser.write(chunk);
109
+ ```ts
110
+ interface XmlNode {
111
+ tagName: string; // element name
112
+ children?: XmlNode[]; // present only when the element has child elements
113
+ text?: string; // present only for text-only elements
114
+ [attribute: string]: any; // attributes, flat on the node
128
115
  }
129
- parser.end();
130
116
  ```
131
117
 
132
- `write()` consumes everything complete in the buffer and keeps the rest for the
133
- next call, so a chunk boundary can fall inside a tag, an attribute value, a text
134
- run, a comment or a `]]>` terminator without changing the result: parsing a
135
- document whole or in 1-byte chunks produces the same tree (the suite checks every
136
- split point).
118
+ An element is emitted on close after its children are attached, so
119
+ `node.children` is complete inside a close listener. Attributes whose names
120
+ collide with `tagName`, `children` or `text` are shadowed by those fields.
137
121
 
138
- `retain` is what makes streaming bounded:
122
+ ### Streaming and retention
123
+
124
+ `write(chunk)` accepts a document in pieces; chunks may split anywhere, and
125
+ `end()` closes whatever is still open and returns the roots.
139
126
 
140
127
  | `retain` | keeps | `end()` returns | memory |
141
128
  | --- | --- | --- | --- |
@@ -143,159 +130,72 @@ split point).
143
130
  | `'root'` | the root being built | `[]`; a finished root is emitted and dropped | largest root |
144
131
  | `'none'` | nothing | `[]`; every element is emitted and dropped | nesting depth |
145
132
 
146
- Measured on a 60 MiB document (ten concatenated extracts, 327k elements):
147
- `retain: 'all'` holds 302 MiB, `retain: 'none'` holds ~0 MiB, both emit the same
148
- 327k elements. Without listeners, `retain: 'none'` still parses - it just throws
149
- the result away.
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:
133
+ With `retain: 'none'`, `keep` restores the subtree of the elements you want, so
134
+ a streamed element arrives complete rather than as a bare tag:
153
135
 
154
136
  ```js
155
137
  new XmlParser({
156
138
  progressive: true,
157
139
  retain: 'none',
158
- keep: ['testsuites.testsuite.testcase'], // 'testsuites.testsuite.testcase' or '</testsuites.testsuite.testcase>'
140
+ keep: ['testsuites.testsuite.testcase'],
159
141
  });
160
142
  ```
161
143
 
162
- Memory is then the largest matched element, not the document. `createStream`
163
- sets this up for you from its `event` path.
144
+ `createStream` applies `keep` for you from its `event` path.
164
145
 
165
- ## Events
146
+ ### Events
166
147
 
167
148
  | form | fires |
168
149
  | --- | --- |
169
150
  | `'<osm.way>'` | when a `<way>` inside `<osm>` opens |
170
151
  | `'</osm.way>'` | when it closes, with `children` populated |
171
152
 
172
- The path is the dotted chain of ancestors. Event names are only built for paths
173
- that have a listener, so subscribing to one path costs nothing for the elements
174
- you ignore. Filter inside the listener:
153
+ The path is the dotted chain from the document root, not just the element name:
154
+ `'</osm.way>'`, never `'</way>'`. The same applies to `keep`. Filter inside the
155
+ listener:
175
156
 
176
157
  ```js
177
158
  parser.on('</osm.way>', (way) => {
178
- if (!way.tags) return; // or any other predicate
159
+ if (!way.tags) return;
179
160
  });
180
161
  ```
181
162
 
182
- ## Options
163
+ ### Options
183
164
 
184
165
  | option | default | meaning |
185
166
  | --- | --- | --- |
186
- | `progressive` | `false` | emit events. Without it, `parse()` only builds the tree |
187
- | `queryParent` | `false` | track parents so `parser.getParent(node)` works (costs a `WeakMap`) |
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 |
167
+ | `progressive` | `false` | emit events; without it `parse()` only builds the tree |
168
+ | `queryParent` | `false` | track parents for `parser.getParent(node)` |
169
+ | `retain` | `'all'` | `'all'`, `'root'` or `'none'` (see [Streaming and retention](#streaming-and-retention)) |
170
+ | `keep` | `[]` | under `retain: 'none'`, paths whose elements keep their subtree |
190
171
 
191
- `parse(xml, parent?, dir?)` also accepts an existing node to attach the parsed
192
- roots to, and a dotted path prefix used when building event names.
172
+ `parse(xml, parent?, dir?)` can attach the parsed roots to an existing node and
173
+ prefix event paths.
193
174
 
194
- The parser is non-validating and never throws on malformed input: unterminated
195
- tags, comments, processing instructions and doctypes are skipped, and elements
196
- still open at end of input are closed so a truncated download yields the
197
- document parsed so far (`'<r><a>text'` returns `r` with an `a` child whose
198
- `text` is `'text'`).
199
-
200
- CDATA sections are content, not markup: their text is kept verbatim and is not
201
- entity-decoded, while text outside them is decoded (the five predefined
202
- entities and numeric character references).
175
+ Malformed input never throws: comments, processing instructions and doctypes
176
+ are skipped, and elements still open at end of input are closed, so a truncated
177
+ download yields the document parsed so far. CDATA content is kept verbatim and
178
+ not entity-decoded; text outside CDATA is.
203
179
 
204
180
  ## Benchmarks
205
181
 
206
- Six generated shapes, each isolating one cost (per-element, per-child, text,
207
- nesting). Methodology, per-shape tables and caveats live in
208
- [`bench/RESULTS.md`](bench/RESULTS.md); rerun with `npm run bench:data && npm run bench`.
209
-
210
- Realistic Overpass-style document, 6.0 MiB, Apple M4 Pro / Node 26:
182
+ Speed on six generated shapes (Overpass-style documents, children-heavy, text,
183
+ nesting), against `txml`, `fast-xml-parser`, `sax` and `htmlparser2`:
211
184
 
212
185
  | workload | xml-event-parser | txml | fast-xml-parser | sax | htmlparser2 |
213
186
  | --- | --- | --- | --- | --- | --- |
214
187
  | tree (parse + walk) | **26.0 ms, 230 MB/s** | 50.6 ms, 118 MB/s | 167.8 ms, 36 MB/s | — | — |
215
- | tree, retained heap | **30.0 MiB** | 42.5 MiB | 35.2 MiB | — | — |
216
188
  | element callbacks | **27.8 ms, 215 MB/s** | — | — | 100.0 ms, 60 MB/s | 44.9 ms, 133 MB/s |
217
189
  | 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 |
218
190
 
219
- Notes on reading the table:
220
-
221
- - The callback and streamed rows for `xml-event-parser` use `retain: 'none'`, so
222
- they do not build a document either; the tree row is the only one that holds
223
- memory. `txml`'s streaming API is a Node stream (`transformStream`), measured
224
- separately and asynchronously.
225
- - Text-heavy documents with many entities and CDATA used to be the weak shape;
226
- replacing the regex decoder with a manual scan took it from 325 to 442 MB/s,
227
- ahead of `txml` (382 MB/s).
228
-
229
- ## Status
230
-
231
- Extracted from [osm2geojson-lite](https://github.com/tibetty/osm2geojson-lite),
232
- where this parser served until 2.2.0 and was then replaced by a purpose-built
233
- OSM scanner. The code and its original unit tests are unchanged apart from the
234
- public naming (`children`/`tagName`/`text` instead of `$innerNodes`/`$tag`/
235
- `$innerText`) and the removal of the `new Function`-based conditioned-path
236
- events.
237
-
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`.
243
-
244
- ## Development
245
-
246
- ```sh
247
- npm ci
248
- npm test # unit tests
249
- npm run typecheck
250
- npm run build # dist/index.mjs, dist/index.cjs, dist/index.d.ts
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
253
- npm run bench # compare against txml / sax / htmlparser2 / fast-xml-parser
254
- ```
255
-
256
- ## Releasing
257
-
258
- `release.yml` publishes with npm trusted publishing (OIDC) and is gated on the
259
- repository variable `NPM_TRUSTED_PUBLISHER`, which stays unset until a trusted
260
- publisher exists.
261
-
262
- One-time bootstrap, because npm cannot attach a trusted publisher to a package
263
- that does not exist yet:
264
-
265
- 1. Publish the first version locally:
266
-
267
- ```sh
268
- npm login
269
- npm publish --access public
270
- ```
271
-
272
- 2. npmjs.com -> Packages -> `xml-event-parser` -> Settings -> **Trusted
273
- publishing** -> GitHub Actions:
274
-
275
- | field | value |
276
- | --- | --- |
277
- | Organization or user | `tibetty` |
278
- | Repository | `xml-event-parser` |
279
- | Workflow filename | `release.yml` (filename only, case-sensitive) |
280
- | Environment name | leave empty |
281
- | Allowed actions | tick **npm publish** |
282
-
283
- Configurations created after September 2026 default to `npm stage publish`
284
- only, so `npm publish` has to be ticked explicitly. The first publish through
285
- that workflow must complete within **two days**, or the configuration expires.
286
-
287
- 3. GitHub -> Settings -> Secrets and variables -> Actions -> Variables: add
288
- `NPM_TRUSTED_PUBLISHER` = `true`.
191
+ Full tables, methodology and caveats: [`bench/RESULTS.md`](bench/RESULTS.md).
289
192
 
290
- 4. Optional hardening, once a workflow publish has succeeded: npmjs.com ->
291
- package Settings -> Publishing access -> *Require two-factor authentication
292
- and disallow tokens*.
193
+ ## Contributing
293
194
 
294
- After that, changing `version` in `package.json` on `main` publishes through
295
- `release.yml`, tags `v<version>` and creates the GitHub release. npm generates
296
- provenance automatically only for a public repository; from a private one the
297
- package is published without an attestation.
195
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development commands, tests,
196
+ benchmarks and the release process.
298
197
 
299
198
  ## License
300
199
 
301
- MIT. Includes work contributed to `osm2geojson-lite` by Harel Mazor.
200
+ MIT. Includes work contributed to
201
+ [osm2geojson-lite](https://github.com/tibetty/osm2geojson-lite) by Harel Mazor.
package/dist/stream.cjs CHANGED
@@ -1 +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;
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.charCodeAt(0)===60?e:`</`+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.mjs CHANGED
@@ -1 +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};
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.charCodeAt(0)===60?e:`</`+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.2.0",
3
+ "version": "0.2.2",
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",