@nodable/flexible-xml-parser 1.0.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/CHANGELOG.md +0 -0
- package/LICENSE +21 -0
- package/README.md +284 -0
- package/lib/fxp.d.cts +652 -0
- package/package.json +80 -0
- package/src/AttributeProcessor.js +107 -0
- package/src/AutoCloseHandler.js +257 -0
- package/src/CharsSymbol.js +16 -0
- package/src/DocTypeReader.js +522 -0
- package/src/InputSource/BufferSource.js +228 -0
- package/src/InputSource/FeedableSource.js +340 -0
- package/src/InputSource/StreamSource.js +49 -0
- package/src/InputSource/StringSource.js +225 -0
- package/src/OptionsBuilder.js +400 -0
- package/src/ParseError.js +91 -0
- package/src/StopNodeProcessor.js +573 -0
- package/src/XMLParser.js +293 -0
- package/src/Xml2JsParser.js +573 -0
- package/src/XmlPartReader.js +183 -0
- package/src/XmlSpecialTagsReader.js +82 -0
- package/src/fxp.d.ts +619 -0
- package/src/fxp.js +8 -0
- package/src/util.js +58 -0
package/lib/fxp.d.cts
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flex XML Parser — TypeScript Definitions (CommonJS)
|
|
3
|
+
*
|
|
4
|
+
* This file mirrors src/fxp.d.ts for consumers using `require()`.
|
|
5
|
+
* It is referenced from the `exports["."].require.types` field in package.json.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Object form of a skip-tag entry — allows per-node control of nested depth
|
|
10
|
+
* tracking and enclosure skipping when scanning for the closing tag.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { xmlEnclosures } from 'flex-xml-parser';
|
|
14
|
+
*
|
|
15
|
+
* const parser = new XMLParser({
|
|
16
|
+
* skip: {
|
|
17
|
+
* tags: [
|
|
18
|
+
* "..secret",
|
|
19
|
+
* { expression: "root.internal", nested: true, skipEnclosures: [...xmlEnclosures] },
|
|
20
|
+
* ]
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
interface SkipTagEntry {
|
|
26
|
+
/** Path expression (same syntax as string skip-tag entries). */
|
|
27
|
+
expression: string;
|
|
28
|
+
/**
|
|
29
|
+
* When true, nested same-name open tags are tracked and the skip ends only
|
|
30
|
+
* when the outermost closing tag is found. Default: false.
|
|
31
|
+
*/
|
|
32
|
+
nested?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Enclosure pairs to skip while scanning for the closing tag.
|
|
35
|
+
* Checked in array order — first open match wins.
|
|
36
|
+
* Defaults to `[]` (plain first-match, no enclosure awareness).
|
|
37
|
+
*/
|
|
38
|
+
skipEnclosures?: Enclosure[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface SkipOptions {
|
|
42
|
+
/** Skip XML declaration `<?xml ... ?>` from output. Default: false */
|
|
43
|
+
declaration?: boolean;
|
|
44
|
+
/** Skip processing instructions (other than declaration) from output. Default: false */
|
|
45
|
+
pi?: boolean;
|
|
46
|
+
/** Skip all attributes from output. Default: true */
|
|
47
|
+
attributes?: boolean;
|
|
48
|
+
/** Exclude CDATA sections entirely from output. Default: false */
|
|
49
|
+
cdata?: boolean;
|
|
50
|
+
/** Exclude comments entirely from output. Default: false */
|
|
51
|
+
comment?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Strip namespace prefixes from tag and attribute names.
|
|
54
|
+
* E.g. `ns:tag` → `tag`, `xmlns:*` attributes are dropped.
|
|
55
|
+
* Default: false
|
|
56
|
+
*/
|
|
57
|
+
nsPrefix?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Tag paths whose entire subtree is silently dropped from output.
|
|
60
|
+
* The parser advances past the closing tag using the same raw-collection
|
|
61
|
+
* mechanism as stop nodes, then discards the content without calling
|
|
62
|
+
* any output builder methods.
|
|
63
|
+
*
|
|
64
|
+
* Each entry is either:
|
|
65
|
+
* - A plain string path expression — equivalent to `{ expression, nested: false, skipEnclosures: [] }`.
|
|
66
|
+
* The very first `</tagName>` ends collection.
|
|
67
|
+
* - A `SkipTagEntry` object with optional `nested` and `skipEnclosures`.
|
|
68
|
+
*
|
|
69
|
+
* Supports path-expression-matcher syntax. Default: []
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* import { xmlEnclosures } from 'flex-xml-parser';
|
|
73
|
+
*
|
|
74
|
+
* skip: {
|
|
75
|
+
* tags: [
|
|
76
|
+
* "..secret",
|
|
77
|
+
* { expression: "root.internal", nested: true, skipEnclosures: [...xmlEnclosures] },
|
|
78
|
+
* ]
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
tags?: Array<string | SkipTagEntry>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface NameForOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Property name for mixed text content when a tag contains both text and child elements.
|
|
87
|
+
* Default: '#text'
|
|
88
|
+
*/
|
|
89
|
+
text?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Property name for CDATA sections.
|
|
92
|
+
* Empty string (default) merges CDATA content into the tag's text value.
|
|
93
|
+
*/
|
|
94
|
+
cdata?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Property name for XML comments.
|
|
97
|
+
* Empty string (default) omits comments from output.
|
|
98
|
+
* Set e.g. '#comment' to capture them.
|
|
99
|
+
*/
|
|
100
|
+
comment?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface AttributeOptions {
|
|
104
|
+
/** Allow boolean (valueless) attributes — treated as `true`. Default: false */
|
|
105
|
+
booleanType?: boolean;
|
|
106
|
+
/** Group all attributes under this property name. Empty string = inline with tag. Default: '' */
|
|
107
|
+
groupBy?: string;
|
|
108
|
+
/** Prefix prepended to attribute names in output. Default: '@_' */
|
|
109
|
+
prefix?: string;
|
|
110
|
+
/** Suffix appended to attribute names in output. Default: '' */
|
|
111
|
+
suffix?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* An open/close pair that defines a region the stop-node processor should skip
|
|
116
|
+
* when scanning for the closing tag. Anything between `open` and `close` is
|
|
117
|
+
* treated as opaque text — closing-tag detection and depth tracking are
|
|
118
|
+
* suspended until `close` is found.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* { open: '<!--', close: '-->' } // XML comment
|
|
122
|
+
* { open: '"', close: '"' } // double-quoted string
|
|
123
|
+
*/
|
|
124
|
+
interface Enclosure {
|
|
125
|
+
open: string;
|
|
126
|
+
close: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Object form of a stop-node entry — allows per-node control of which
|
|
131
|
+
* enclosures the processor should skip when scanning for the closing tag.
|
|
132
|
+
*
|
|
133
|
+
* ```ts
|
|
134
|
+
* import { xmlEnclosures, quoteEnclosures } from 'flex-xml-parser';
|
|
135
|
+
*
|
|
136
|
+
* const parser = new XMLParser({
|
|
137
|
+
* tags: {
|
|
138
|
+
* stopNodes: [
|
|
139
|
+
* "..script", // plain — no enclosures
|
|
140
|
+
* { expression: "body..pre", skipEnclosures: [...xmlEnclosures] },
|
|
141
|
+
* { expression: "head..style", skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] },
|
|
142
|
+
* ]
|
|
143
|
+
* }
|
|
144
|
+
* });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
interface StopNodeEntry {
|
|
148
|
+
/** Path expression (same syntax as string stop-node entries). */
|
|
149
|
+
expression: string;
|
|
150
|
+
/**
|
|
151
|
+
* When true, nested same-name open tags are tracked and the stop node ends
|
|
152
|
+
* only when the outermost closing tag is found. Default: false.
|
|
153
|
+
*/
|
|
154
|
+
nested?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Enclosure pairs to skip while scanning for the closing tag.
|
|
157
|
+
* Checked in array order — first open match wins.
|
|
158
|
+
* Defaults to `[]` (plain first-match, no depth tracking).
|
|
159
|
+
*/
|
|
160
|
+
skipEnclosures: Enclosure[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface TagOptions {
|
|
164
|
+
/** Tags that never have a closing tag (e.g. ['br', 'img', 'hr']). Default: [] */
|
|
165
|
+
unpaired?: string[];
|
|
166
|
+
/**
|
|
167
|
+
* Tag paths whose content is captured raw without further XML parsing.
|
|
168
|
+
*
|
|
169
|
+
* Each entry is either:
|
|
170
|
+
* - A plain string path expression — equivalent to `{ expression, skipEnclosures: [] }`.
|
|
171
|
+
* The very first `</tagName>` ends collection (no depth tracking, no enclosure skipping).
|
|
172
|
+
* - A `StopNodeEntry` object with an explicit `skipEnclosures` array.
|
|
173
|
+
* When `skipEnclosures` is non-empty, depth tracking is enabled and anything
|
|
174
|
+
* between an enclosure's open/close markers is skipped (so false closing tags
|
|
175
|
+
* inside comments, CDATA, string literals, etc. are ignored).
|
|
176
|
+
*
|
|
177
|
+
* Supports path-expression-matcher syntax. Default: []
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* import { xmlEnclosures, quoteEnclosures } from 'flex-xml-parser';
|
|
181
|
+
*
|
|
182
|
+
* stopNodes: [
|
|
183
|
+
* "..script", // plain
|
|
184
|
+
* { expression: "body..pre", skipEnclosures: [...xmlEnclosures] },
|
|
185
|
+
* { expression: "head..style", skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] },
|
|
186
|
+
* ]
|
|
187
|
+
*/
|
|
188
|
+
stopNodes?: Array<string | StopNodeEntry>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Options for DOCTYPE reading — controls whether entities are collected
|
|
193
|
+
* and enforces read-time security limits.
|
|
194
|
+
*/
|
|
195
|
+
interface DoctypeOptions {
|
|
196
|
+
/**
|
|
197
|
+
* Whether to collect entities declared in the DOCTYPE internal subset and
|
|
198
|
+
* forward them to the output builder for replacement.
|
|
199
|
+
* The DOCTYPE block is always read to consume it; this flag controls forwarding.
|
|
200
|
+
*/
|
|
201
|
+
enabled?: boolean;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Max number of entities that may be declared in a DOCTYPE internal subset.
|
|
205
|
+
* Enforced by DocTypeReader at declaration time.
|
|
206
|
+
* Default: 100
|
|
207
|
+
*/
|
|
208
|
+
maxEntityCount?: number;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Max bytes per entity definition value in DOCTYPE.
|
|
212
|
+
* Enforced by DocTypeReader at declaration time.
|
|
213
|
+
* Default: 10000
|
|
214
|
+
*/
|
|
215
|
+
maxEntitySize?: number;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── Error handling ────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* All error codes thrown by the parser.
|
|
222
|
+
* Use with `instanceof ParseError` and `err.code === ErrorCode.XXX` for
|
|
223
|
+
* precise error handling without string-matching against messages.
|
|
224
|
+
*/
|
|
225
|
+
declare const ErrorCode: {
|
|
226
|
+
// Input type errors
|
|
227
|
+
readonly INVALID_INPUT: 'INVALID_INPUT';
|
|
228
|
+
readonly INVALID_STREAM: 'INVALID_STREAM';
|
|
229
|
+
|
|
230
|
+
// Streaming / feed API
|
|
231
|
+
readonly ALREADY_STREAMING: 'ALREADY_STREAMING';
|
|
232
|
+
readonly NOT_STREAMING: 'NOT_STREAMING';
|
|
233
|
+
readonly DATA_MUST_BE_STRING: 'DATA_MUST_BE_STRING';
|
|
234
|
+
|
|
235
|
+
// Tag structure
|
|
236
|
+
readonly UNEXPECTED_END: 'UNEXPECTED_END';
|
|
237
|
+
readonly UNEXPECTED_CLOSE_TAG: 'UNEXPECTED_CLOSE_TAG';
|
|
238
|
+
readonly MISMATCHED_CLOSE_TAG: 'MISMATCHED_CLOSE_TAG';
|
|
239
|
+
readonly UNEXPECTED_TRAILING_DATA: 'UNEXPECTED_TRAILING_DATA';
|
|
240
|
+
readonly INVALID_TAG: 'INVALID_TAG';
|
|
241
|
+
readonly UNCLOSED_QUOTE: 'UNCLOSED_QUOTE';
|
|
242
|
+
|
|
243
|
+
// Namespace
|
|
244
|
+
readonly MULTIPLE_NAMESPACES: 'MULTIPLE_NAMESPACES';
|
|
245
|
+
|
|
246
|
+
// Security
|
|
247
|
+
readonly SECURITY_PROTOTYPE_POLLUTION: 'SECURITY_PROTOTYPE_POLLUTION';
|
|
248
|
+
readonly SECURITY_RESERVED_OPTION: 'SECURITY_RESERVED_OPTION';
|
|
249
|
+
readonly SECURITY_RESTRICTED_NAME: 'SECURITY_RESTRICTED_NAME';
|
|
250
|
+
|
|
251
|
+
// Limits (DoS prevention)
|
|
252
|
+
readonly LIMIT_MAX_NESTED_TAGS: 'LIMIT_MAX_NESTED_TAGS';
|
|
253
|
+
readonly LIMIT_MAX_ATTRIBUTES: 'LIMIT_MAX_ATTRIBUTES';
|
|
254
|
+
|
|
255
|
+
// Entity limits
|
|
256
|
+
readonly ENTITY_MAX_COUNT: 'ENTITY_MAX_COUNT';
|
|
257
|
+
readonly ENTITY_MAX_SIZE: 'ENTITY_MAX_SIZE';
|
|
258
|
+
readonly ENTITY_MAX_EXPANSIONS: 'ENTITY_MAX_EXPANSIONS';
|
|
259
|
+
readonly ENTITY_MAX_EXPANDED_LENGTH: 'ENTITY_MAX_EXPANDED_LENGTH';
|
|
260
|
+
|
|
261
|
+
// Entity registration
|
|
262
|
+
readonly ENTITY_INVALID_KEY: 'ENTITY_INVALID_KEY';
|
|
263
|
+
readonly ENTITY_INVALID_VALUE: 'ENTITY_INVALID_VALUE';
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
type ErrorCodeValue = typeof ErrorCode[keyof typeof ErrorCode];
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Structured error class thrown by all parser error paths.
|
|
270
|
+
*
|
|
271
|
+
* Always catch with `instanceof ParseError` to distinguish library errors
|
|
272
|
+
* from unexpected runtime errors:
|
|
273
|
+
*
|
|
274
|
+
* ```ts
|
|
275
|
+
* try {
|
|
276
|
+
* parser.parse(xml);
|
|
277
|
+
* } catch (e) {
|
|
278
|
+
* if (e instanceof ParseError) {
|
|
279
|
+
* console.error(e.code, e.line, e.col, e.message);
|
|
280
|
+
* } else {
|
|
281
|
+
* throw e; // unexpected runtime error
|
|
282
|
+
* }
|
|
283
|
+
* }
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
declare class ParseError extends Error {
|
|
287
|
+
readonly name: 'ParseError';
|
|
288
|
+
|
|
289
|
+
/** Machine-readable error code. Always one of the `ErrorCode` values. */
|
|
290
|
+
readonly code: ErrorCodeValue;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 1-based line number where the error occurred.
|
|
294
|
+
* `undefined` when position information is not available for this error type.
|
|
295
|
+
*/
|
|
296
|
+
readonly line: number | undefined;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 1-based column where the error occurred.
|
|
300
|
+
* `undefined` when position information is not available for this error type.
|
|
301
|
+
*/
|
|
302
|
+
readonly col: number | undefined;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 0-based character offset from the start of the document.
|
|
306
|
+
* `undefined` when position information is not available for this error type.
|
|
307
|
+
*/
|
|
308
|
+
readonly index: number | undefined;
|
|
309
|
+
|
|
310
|
+
constructor(
|
|
311
|
+
message: string,
|
|
312
|
+
code: ErrorCodeValue,
|
|
313
|
+
position?: { line?: number; col?: number; index?: number }
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
/** Returns a formatted string: `ParseError [CODE] at line N, col M: message` */
|
|
317
|
+
toString(): string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ─── Limits ────────────────────────────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Structural limits that guard against resource-exhaustion and DoS attacks.
|
|
324
|
+
* All properties default to `null` (no limit enforced).
|
|
325
|
+
*
|
|
326
|
+
* Errors thrown when limits are exceeded are always `ParseError` instances
|
|
327
|
+
* with codes `LIMIT_MAX_NESTED_TAGS` or `LIMIT_MAX_ATTRIBUTES` respectively,
|
|
328
|
+
* and carry `line`, `col`, and `index` position information.
|
|
329
|
+
*/
|
|
330
|
+
interface LimitsOptions {
|
|
331
|
+
/**
|
|
332
|
+
* Maximum tag nesting depth.
|
|
333
|
+
*
|
|
334
|
+
* Throws `ParseError` with code `LIMIT_MAX_NESTED_TAGS` when a tag would
|
|
335
|
+
* open at a depth greater than this value.
|
|
336
|
+
*
|
|
337
|
+
* Prevents stack-overflow attacks via pathologically deep XML such as
|
|
338
|
+
* `<a><a><a>...</a></a></a>` (1 million levels deep).
|
|
339
|
+
*
|
|
340
|
+
* Must be a positive integer (`>= 1`) or `null`.
|
|
341
|
+
* Default: `null` (unlimited)
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* // Reject XML deeper than 100 tags
|
|
345
|
+
* new XMLParser({ limits: { maxNestedTags: 100 } });
|
|
346
|
+
*/
|
|
347
|
+
maxNestedTags?: number | null;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Maximum number of attributes allowed on a single tag.
|
|
351
|
+
*
|
|
352
|
+
* Throws `ParseError` with code `LIMIT_MAX_ATTRIBUTES` when a tag has
|
|
353
|
+
* more attributes than this value. Only enforced when `skip.attributes`
|
|
354
|
+
* is `false` (attributes are being parsed).
|
|
355
|
+
*
|
|
356
|
+
* Prevents attacks that use thousands of attributes to exhaust memory or
|
|
357
|
+
* CPU during attribute parsing.
|
|
358
|
+
*
|
|
359
|
+
* Must be a non-negative integer (`>= 0`) or `null`.
|
|
360
|
+
* `0` means no attributes are permitted on any tag.
|
|
361
|
+
* Default: `null` (unlimited)
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* // Reject any tag with more than 50 attributes
|
|
365
|
+
* new XMLParser({ skip: { attributes: false }, limits: { maxAttributesPerTag: 50 } });
|
|
366
|
+
*/
|
|
367
|
+
maxAttributesPerTag?: number | null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* A value parser transforms a value in the parsing chain.
|
|
372
|
+
* Receives the current value and an optional context object.
|
|
373
|
+
*/
|
|
374
|
+
interface ValueParser {
|
|
375
|
+
/**
|
|
376
|
+
* @param val Current value (string initially; may already be typed if earlier parsers ran)
|
|
377
|
+
* @param context { tagName, isAttribute, attrName? }
|
|
378
|
+
*/
|
|
379
|
+
parse(val: any, context?: { tagName: string; isAttribute: boolean; attrName?: string }): any;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Buffer options for the feed()/end() and parseStream() input APIs.
|
|
384
|
+
* Passed as `feedable` inside XMLParser options.
|
|
385
|
+
*/
|
|
386
|
+
interface FeedableOptions {
|
|
387
|
+
/**
|
|
388
|
+
* Maximum number of characters allowed in the buffer at any one time.
|
|
389
|
+
* Prevents memory exhaustion when data is fed faster than it is consumed.
|
|
390
|
+
* Default: 10485760 (10 MB)
|
|
391
|
+
*/
|
|
392
|
+
maxBufferSize?: number;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* When true (default), already-processed characters are automatically
|
|
396
|
+
* discarded from the buffer once the processed portion exceeds
|
|
397
|
+
* flushThreshold. Keeps memory usage flat for large documents.
|
|
398
|
+
* Default: true
|
|
399
|
+
*/
|
|
400
|
+
autoFlush?: boolean;
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Number of processed characters that triggers an automatic flush.
|
|
404
|
+
* Lower values free memory sooner at the cost of more string-slice
|
|
405
|
+
* operations. Default: 1024 (1 KB)
|
|
406
|
+
*/
|
|
407
|
+
flushThreshold?: number;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
interface X2jOptions {
|
|
411
|
+
// --- node-type controls ---
|
|
412
|
+
/** Fine-grained control over which node types appear in output */
|
|
413
|
+
skip?: SkipOptions;
|
|
414
|
+
|
|
415
|
+
// --- property name mapping ---
|
|
416
|
+
/** Property names used for special nodes in output */
|
|
417
|
+
nameFor?: NameForOptions;
|
|
418
|
+
|
|
419
|
+
// --- attribute controls ---
|
|
420
|
+
/** Attribute parsing and representation options */
|
|
421
|
+
attributes?: AttributeOptions;
|
|
422
|
+
|
|
423
|
+
// --- tag controls ---
|
|
424
|
+
/** Tag parsing options including stop nodes and value parser chain */
|
|
425
|
+
tags?: TagOptions;
|
|
426
|
+
|
|
427
|
+
// --- DOCTYPE parsing ---
|
|
428
|
+
/**
|
|
429
|
+
* Controls whether DOCTYPE entities are collected and read-time security limits.
|
|
430
|
+
* Once collected will be passed to Output builder to take any decision
|
|
431
|
+
*/
|
|
432
|
+
doctypeOptions?: DoctypeOptions;
|
|
433
|
+
|
|
434
|
+
// --- security ---
|
|
435
|
+
/** Throw when a tag/attribute name collides with a nameFor.* or attributes.groupBy value. Default: false */
|
|
436
|
+
strictReservedNames?: boolean;
|
|
437
|
+
/** Custom handler for dangerous (non-critical) property names. Default: prefix with '__' */
|
|
438
|
+
onDangerousProperty?: (name: string) => string;
|
|
439
|
+
|
|
440
|
+
// --- filtering (path-expression-matcher) ---
|
|
441
|
+
select?: string[];
|
|
442
|
+
only?: string[];
|
|
443
|
+
|
|
444
|
+
// --- limits (DoS prevention) ---
|
|
445
|
+
/**
|
|
446
|
+
* Structural limits that guard against resource-exhaustion attacks.
|
|
447
|
+
* All properties default to `null` (no limit enforced).
|
|
448
|
+
*
|
|
449
|
+
* ```ts
|
|
450
|
+
* new XMLParser({
|
|
451
|
+
* limits: {
|
|
452
|
+
* maxNestedTags: 100, // reject XML deeper than 100 levels
|
|
453
|
+
* maxAttributesPerTag: 50, // reject any tag with > 50 attributes
|
|
454
|
+
* }
|
|
455
|
+
* });
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
limits?: LimitsOptions | null;
|
|
459
|
+
|
|
460
|
+
// --- feedable (feed/end and parseStream buffer options) ---
|
|
461
|
+
/**
|
|
462
|
+
* Buffer behaviour for the FeedableSource (feed/end API) and StreamSource
|
|
463
|
+
* (parseStream API). All properties have sensible defaults and only need
|
|
464
|
+
* to be set when processing very large documents or operating under tight
|
|
465
|
+
* memory constraints.
|
|
466
|
+
*/
|
|
467
|
+
feedable?: FeedableOptions;
|
|
468
|
+
|
|
469
|
+
// --- output builder ---
|
|
470
|
+
/** Pluggable output builder instance. Default: CompactObjBuilder */
|
|
471
|
+
OutputBuilder?: OutputBuilderFactory | null;
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Callback fired by `NodeTreeBuilder` and `CompactObjBuilder` whenever a stop node
|
|
475
|
+
* is fully collected, before the raw content is added to the output tree.
|
|
476
|
+
*
|
|
477
|
+
* Receive the tag detail, the raw unparsed content, and a read-only path
|
|
478
|
+
* matcher. Useful for side-channel analysis (e.g. extracting script content
|
|
479
|
+
* from HTML) without having to post-process the output tree.
|
|
480
|
+
*
|
|
481
|
+
* The callback is informational — return value is ignored. To suppress the
|
|
482
|
+
* node from output, use a custom OutputBuilder subclass instead.
|
|
483
|
+
*
|
|
484
|
+
* @param tagDetail - `{ name, line, col, index }` of the stop-node opening tag.
|
|
485
|
+
* @param rawContent - Raw text content between the opening and closing tags.
|
|
486
|
+
* @param matcher - Read-only path matcher positioned at the stop node.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* const scripts: string[] = [];
|
|
490
|
+
* const parser = new XMLParser({
|
|
491
|
+
* tags: { stopNodes: ["..script"] },
|
|
492
|
+
* onStopNode(tagDetail, rawContent, matcher) {
|
|
493
|
+
* scripts.push(rawContent);
|
|
494
|
+
* }
|
|
495
|
+
* });
|
|
496
|
+
*/
|
|
497
|
+
onStopNode?: (
|
|
498
|
+
tagDetail: { name: string; line: number; col: number; index: number },
|
|
499
|
+
rawContent: string,
|
|
500
|
+
matcher: any,
|
|
501
|
+
) => void;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
interface OutputBuilderFactory {
|
|
505
|
+
getInstance(parserOptions: X2jOptions): OutputBuilderInstance;
|
|
506
|
+
registerValueParser(name: string, parser: ValueParser): void;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
interface OutputBuilderInstance {
|
|
510
|
+
addElement(tag: { name: string }, matcher: any): void;
|
|
511
|
+
closeElement(matcher: any): void;
|
|
512
|
+
addValue(text: string, matcher: any): void;
|
|
513
|
+
addAttribute(name: string, value: any): void;
|
|
514
|
+
addComment(text: string): void;
|
|
515
|
+
addLiteral(text: string): void;
|
|
516
|
+
addDeclaration(): void;
|
|
517
|
+
addInstruction(name: string): void;
|
|
518
|
+
getOutput(): any;
|
|
519
|
+
registeredValParsers: Record<string, ValueParser>;
|
|
520
|
+
/**
|
|
521
|
+
* Optional hook called by the parser when a stop node is fully collected.
|
|
522
|
+
* Implement this in custom OutputBuilder classes to handle stop-node content.
|
|
523
|
+
* `NodeTreeBuilder` and `CompactObjBuilder` implement it and delegate to the
|
|
524
|
+
* `options.onStopNode` callback when supplied.
|
|
525
|
+
*/
|
|
526
|
+
onStopNode?(
|
|
527
|
+
tagDetail: { name: string; line: number; col: number; index: number },
|
|
528
|
+
rawContent: string,
|
|
529
|
+
matcher: any,
|
|
530
|
+
): void;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
default class XMLParser {
|
|
534
|
+
/**
|
|
535
|
+
* Create a new XMLParser.
|
|
536
|
+
* @throws {ParseError} with code `INVALID_INPUT` or `SECURITY_RESERVED_OPTION`
|
|
537
|
+
* if any option value is invalid or contains a reserved property name.
|
|
538
|
+
*/
|
|
539
|
+
constructor(options?: X2jOptions);
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Parse an XML string or Buffer to a JavaScript object.
|
|
543
|
+
* @throws {ParseError} on any well-formedness or limit violation.
|
|
544
|
+
*/
|
|
545
|
+
parse(xmlData: string | Buffer): any;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Parse a Uint8Array / byte array to a JavaScript object.
|
|
549
|
+
* @throws {ParseError} on any well-formedness or limit violation.
|
|
550
|
+
*/
|
|
551
|
+
parseBytesArr(xmlData: Uint8Array | ArrayBufferView): any;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Parse an XML Node.js Readable stream and return a Promise that resolves
|
|
555
|
+
* with the parsed JS object.
|
|
556
|
+
*
|
|
557
|
+
* Chunks are processed incrementally as they arrive — already-consumed input
|
|
558
|
+
* is freed immediately, so memory stays proportional to the largest single
|
|
559
|
+
* token rather than the total document size.
|
|
560
|
+
*
|
|
561
|
+
* @throws {ParseError} with code `INVALID_STREAM` if the argument is not a
|
|
562
|
+
* Node.js Readable stream.
|
|
563
|
+
*/
|
|
564
|
+
parseStream(readable: NodeJS.ReadableStream): Promise<any>;
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Feed an XML data chunk for incremental parsing.
|
|
568
|
+
* Call `end()` when all chunks have been fed.
|
|
569
|
+
* @throws {ParseError} with code `DATA_MUST_BE_STRING` if data is not a string or Buffer.
|
|
570
|
+
*/
|
|
571
|
+
feed(data: string | Buffer): this;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Signal end of input and return the parsed result.
|
|
575
|
+
* @throws {ParseError} with code `NOT_STREAMING` if called before any `feed()`.
|
|
576
|
+
* @throws {ParseError} on any well-formedness or limit violation in the accumulated input.
|
|
577
|
+
*/
|
|
578
|
+
end(): any;
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Return structural errors collected during the last parse call.
|
|
582
|
+
* Only populated when `autoClose.collectErrors` is `true`.
|
|
583
|
+
* Each entry: `{ type, tag, expected, line, col, index }`
|
|
584
|
+
*/
|
|
585
|
+
getParseErrors(): Array<{
|
|
586
|
+
type: 'unclosed-eof' | 'mismatched-close' | 'phantom-close' | 'partial-tag';
|
|
587
|
+
tag: string;
|
|
588
|
+
expected?: string;
|
|
589
|
+
line?: number;
|
|
590
|
+
col?: number;
|
|
591
|
+
index?: number;
|
|
592
|
+
}>;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Returns `true` if the last parse call was terminated early by `exitIf`.
|
|
596
|
+
* Returns `false` when `exitIf` never fired or the feature is not configured.
|
|
597
|
+
*/
|
|
598
|
+
wasExited: boolean;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
{ XMLParser };
|
|
602
|
+
|
|
603
|
+
class CompactObjBuilder implements OutputBuilderFactory {
|
|
604
|
+
constructor(options?: Partial<X2jOptions>);
|
|
605
|
+
getInstance(parserOptions: X2jOptions): OutputBuilderInstance;
|
|
606
|
+
registerValueParser(name: string, parser: ValueParser): void;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ─── Stop-node utilities ───────────────────────────────────────────────────────
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* XML structural enclosures — comments, CDATA sections, processing instructions.
|
|
613
|
+
*
|
|
614
|
+
* Use in `skipEnclosures` to prevent false closing-tag matches inside these
|
|
615
|
+
* XML constructs:
|
|
616
|
+
*
|
|
617
|
+
* ```ts
|
|
618
|
+
* { expression: "body..pre", skipEnclosures: [...xmlEnclosures] }
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
declare const xmlEnclosures: ReadonlyArray<Enclosure>;
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* String-literal enclosures — single-quote, double-quote, and template literals.
|
|
625
|
+
*
|
|
626
|
+
* Use in `skipEnclosures` for stop nodes that contain JS or CSS source code
|
|
627
|
+
* where closing tags might appear inside string literals:
|
|
628
|
+
*
|
|
629
|
+
* ```ts
|
|
630
|
+
* { expression: "head..style", skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] }
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
declare const quoteEnclosures: ReadonlyArray<Enclosure>;
|
|
634
|
+
|
|
635
|
+
declare namespace fxp {
|
|
636
|
+
export {
|
|
637
|
+
XMLParser,
|
|
638
|
+
ErrorCode,
|
|
639
|
+
xmlEnclosures,
|
|
640
|
+
ReplaceEntitiesValueParser,
|
|
641
|
+
ParseError,
|
|
642
|
+
ElementType,
|
|
643
|
+
BaseOutputBuilder,
|
|
644
|
+
booleanParserExt,
|
|
645
|
+
joinParser,
|
|
646
|
+
quoteEnclosures,
|
|
647
|
+
DoctypeOptions
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
export = fxp;
|